代码提交
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
7
.eslintignore
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/dist
|
||||||
|
/src-capacitor
|
||||||
|
/src-cordova
|
||||||
|
/.quasar
|
||||||
|
/node_modules
|
||||||
|
.eslintrc.js
|
||||||
|
/src-ssr
|
90
.eslintrc.cjs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
module.exports = {
|
||||||
|
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
||||||
|
// This option interrupts the configuration hierarchy at this file
|
||||||
|
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
||||||
|
root: true,
|
||||||
|
|
||||||
|
// https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
|
||||||
|
// Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working
|
||||||
|
// `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted
|
||||||
|
parserOptions: {
|
||||||
|
parser: require.resolve('@typescript-eslint/parser'),
|
||||||
|
extraFileExtensions: [ '.vue' ]
|
||||||
|
},
|
||||||
|
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
node: true,
|
||||||
|
'vue/setup-compiler-macros': true
|
||||||
|
},
|
||||||
|
|
||||||
|
// Rules order is important, please avoid shuffling them
|
||||||
|
extends: [
|
||||||
|
// Base ESLint recommended rules
|
||||||
|
// 'eslint:recommended',
|
||||||
|
|
||||||
|
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
|
||||||
|
// ESLint typescript rules
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
|
||||||
|
// Uncomment any of the lines below to choose desired strictness,
|
||||||
|
// but leave only one uncommented!
|
||||||
|
// See https://eslint.vuejs.org/rules/#available-rules
|
||||||
|
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
||||||
|
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||||
|
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||||
|
|
||||||
|
// https://github.com/prettier/eslint-config-prettier#installation
|
||||||
|
// usage with Prettier, provided by 'eslint-config-prettier'.
|
||||||
|
'prettier'
|
||||||
|
],
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
// required to apply rules which need type information
|
||||||
|
'@typescript-eslint',
|
||||||
|
|
||||||
|
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
||||||
|
// required to lint *.vue files
|
||||||
|
'vue'
|
||||||
|
|
||||||
|
// https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
|
||||||
|
// Prettier has not been included as plugin to avoid performance impact
|
||||||
|
// add it as an extension for your IDE
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
globals: {
|
||||||
|
ga: 'readonly', // Google Analytics
|
||||||
|
cordova: 'readonly',
|
||||||
|
__statics: 'readonly',
|
||||||
|
__QUASAR_SSR__: 'readonly',
|
||||||
|
__QUASAR_SSR_SERVER__: 'readonly',
|
||||||
|
__QUASAR_SSR_CLIENT__: 'readonly',
|
||||||
|
__QUASAR_SSR_PWA__: 'readonly',
|
||||||
|
process: 'readonly',
|
||||||
|
Capacitor: 'readonly',
|
||||||
|
chrome: 'readonly'
|
||||||
|
},
|
||||||
|
|
||||||
|
// add your custom rules here
|
||||||
|
rules: {
|
||||||
|
|
||||||
|
'prefer-promise-reject-errors': 'off',
|
||||||
|
|
||||||
|
quotes: ['warn', 'single', { avoidEscape: true }],
|
||||||
|
|
||||||
|
// this rule, if on, would require explicit return type on the `render` function
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
|
||||||
|
// in plain CommonJS modules, you can't use `import foo = require('foo')` to pass this rule, so it has to be disabled
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
|
||||||
|
// The core 'no-unused-vars' rules (in the eslint:recommended ruleset)
|
||||||
|
// does not work with type definitions
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
|
||||||
|
// allow debugger during development only
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||||
|
}
|
||||||
|
}
|
33
.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.DS_Store
|
||||||
|
.thumbs.db
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Quasar core related directories
|
||||||
|
.quasar
|
||||||
|
/dist
|
||||||
|
|
||||||
|
# Cordova related directories and files
|
||||||
|
/src-cordova/node_modules
|
||||||
|
/src-cordova/platforms
|
||||||
|
/src-cordova/plugins
|
||||||
|
/src-cordova/www
|
||||||
|
|
||||||
|
# Capacitor related directories and files
|
||||||
|
/src-capacitor/www
|
||||||
|
/src-capacitor/node_modules
|
||||||
|
|
||||||
|
# BEX related directories and files
|
||||||
|
/src-bex/www
|
||||||
|
/src-bex/js/core
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
2
.gitmodule
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[submodule "graphic-pixi"]
|
||||||
|
branch = bj-rtss
|
7
.gitmodules
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[submodule "bj-rtss-message"]
|
||||||
|
path = bj-rtss-message
|
||||||
|
url = https://git.code.tencent.com/beijing-rtss-test/bj-rtss-message.git
|
||||||
|
[submodule "graphic-pixi"]
|
||||||
|
path = graphic-pixi
|
||||||
|
url = https://git.code.tencent.com/jl-framework/graphic-pixi.git
|
||||||
|
branch = bj-rtss
|
3
.npmrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# pnpm-related options
|
||||||
|
shamefully-hoist=true
|
||||||
|
strict-peer-dependencies=false
|
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": true
|
||||||
|
}
|
15
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"editorconfig.editorconfig",
|
||||||
|
"vue.volar",
|
||||||
|
"wayou.vscode-todo-highlight"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"octref.vetur",
|
||||||
|
"hookyqr.beautify",
|
||||||
|
"dbaeumer.jshint",
|
||||||
|
"ms-vscode.vscode-typescript-tslint-plugin"
|
||||||
|
]
|
||||||
|
}
|
16
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"editor.bracketPairColorization.enabled": true,
|
||||||
|
"editor.guides.bracketPairs": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.codeActionsOnSave": [
|
||||||
|
"source.fixAll.eslint"
|
||||||
|
],
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"vue"
|
||||||
|
],
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
|
}
|
62
README.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# BjRtssClient (bj-rtss-client)
|
||||||
|
|
||||||
|
beijing rtss 测试系统
|
||||||
|
|
||||||
|
## 项目 clone 说明
|
||||||
|
|
||||||
|
此项目包含子模块,拉取方式:
|
||||||
|
|
||||||
|
- 1.在克隆主项目的时候带上参数 --recurse-submodules
|
||||||
|
- 2.或者 clone 完成后,在项目目录中执行:git submodule init && git submodule update
|
||||||
|
|
||||||
|
## 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn
|
||||||
|
# or
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## 编译 proto 文件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn run proto
|
||||||
|
# or
|
||||||
|
npm run proto
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
quasar dev
|
||||||
|
# or
|
||||||
|
yarn run dev
|
||||||
|
# or
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint the files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn lint
|
||||||
|
# or
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format the files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn format
|
||||||
|
# or
|
||||||
|
npm run format
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build the app for production
|
||||||
|
|
||||||
|
```bash
|
||||||
|
quasar build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize the configuration
|
||||||
|
|
||||||
|
See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).
|
1
graphic-pixi
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 7e4eaed0cf06d68c75cb51c30329eff5fe4d1e3f
|
21
index.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<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<% } %>">
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<!-- quasar:entry-point -->
|
||||||
|
</body>
|
||||||
|
</html>
|
52
package.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "bj-rtss-client",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "beijing rtss 测试系统",
|
||||||
|
"productName": "BJRTSSCLIENT",
|
||||||
|
"author": "walker <shengxuqiang@joylink.club>",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"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": "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",
|
||||||
|
"google-protobuf": "^3.21.2",
|
||||||
|
"js-base64": "^3.7.5",
|
||||||
|
"pinia": "^2.0.11",
|
||||||
|
"pixi-viewport": "^5.0.1",
|
||||||
|
"pixi.js": "^7.2.4",
|
||||||
|
"quasar": "^2.6.0",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"vue-router": "^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@quasar/app-vite": "^1.0.0",
|
||||||
|
"@types/google-protobuf": "^3.15.6",
|
||||||
|
"@types/node": "^12.20.21",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||||
|
"@typescript-eslint/parser": "^5.10.0",
|
||||||
|
"autoprefixer": "^10.4.2",
|
||||||
|
"eslint": "^8.10.0",
|
||||||
|
"eslint-config-prettier": "^8.1.0",
|
||||||
|
"eslint-plugin-vue": "^9.0.0",
|
||||||
|
"prettier": "^2.5.1",
|
||||||
|
"protoc-gen-ts": "^0.8.6",
|
||||||
|
"ts-md5": "^1.3.1",
|
||||||
|
"typescript": "^4.5.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || ^16 || ^14.19",
|
||||||
|
"npm": ">= 6.13.4",
|
||||||
|
"yarn": ">= 1.21.1"
|
||||||
|
}
|
||||||
|
}
|
27
postcss.config.cjs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
// https://github.com/postcss/autoprefixer
|
||||||
|
require('autoprefixer')({
|
||||||
|
overrideBrowserslist: [
|
||||||
|
'last 4 Chrome versions',
|
||||||
|
'last 4 Firefox versions',
|
||||||
|
'last 4 Edge versions',
|
||||||
|
'last 4 Safari versions',
|
||||||
|
'last 4 Android versions',
|
||||||
|
'last 4 ChromeAndroid versions',
|
||||||
|
'last 4 FirefoxAndroid versions',
|
||||||
|
'last 4 iOS versions'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// https://github.com/elchininet/postcss-rtlcss
|
||||||
|
// If you want to support RTL css, then
|
||||||
|
// 1. yarn/npm install postcss-rtlcss
|
||||||
|
// 2. optionally set quasar.config.js > framework > lang to an RTL language
|
||||||
|
// 3. uncomment the following line:
|
||||||
|
// require('postcss-rtlcss')
|
||||||
|
]
|
||||||
|
}
|
21
public/drawIcon.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<symbol id="icon-curve" viewBox="0 0 1280 1024">
|
||||||
|
<path
|
||||||
|
d="M1280 614.4c0-32-32-64-64-64-38.4 0-64 32-64 64 0 44.8-12.8 172.8-70.4 236.8-32 32-70.4 44.8-121.6 44.8-89.6 0-153.6-243.2-204.8-416-64-249.6-128-480-294.4-480C224 0 115.2 364.8 44.8 601.6c-12.8 51.2-25.6 96-38.4 121.6-12.8 32 6.4 70.4 38.4 83.2 32 12.8 70.4-6.4 83.2-38.4 12.8-32 25.6-76.8 44.8-134.4C217.6 460.8 320 128 460.8 128 531.2 128 595.2 358.4 640 512c70.4 249.6 140.8 512 326.4 512 83.2 0 153.6-25.6 204.8-83.2 115.2-121.6 108.8-320 108.8-326.4z"></path>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="icon-fans" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M947.303 517.937c-16.16-18.18-39.391-29.289-63.629-29.289h-240.377c-6.061-30.299-21.21-56.56-44.441-75.748l120.189-208.058c12.119-21.21 14.141-46.46 6.061-69.69s-25.249-40.398-47.47-49.49c-53.529-21.21-109.077-31.31-166.649-31.31s-113.118 10.1-166.649 31.31c-22.22 9.090-39.391 27.27-47.47 49.49-8.079 23.23-6.061 48.481 6.061 69.69l120.189 208.058c-22.22 19.188-38.379 46.46-44.441 75.748h-239.367c-24.238 0-47.47 11.109-63.629 29.289-16.16 18.18-22.22 42.42-19.188 66.659 9.090 56.56 27.27 110.087 56.56 159.578 28.278 49.49 65.65 92.917 110.087 128.269 15.149 12.119 33.329 18.18 51.51 18.18 5.050 0 10.1 0 15.149-1.009 24.238-4.041 44.441-19.188 56.56-40.398l120.189-208.058c6.061 2.020 12.119 4.041 19.188 5.050v289.867c0 14.141 11.109 25.249 25.249 25.249s25.249-11.109 25.249-25.249v-289.867c6.061-1.009 13.13-3.028 19.188-5.050l120.189 208.058c12.119 21.21 33.329 35.349 56.56 40.398 5.050 1.009 10.1 1.009 15.149 1.009 18.18 0 36.357-6.061 51.51-18.18 44.441-35.349 81.81-78.778 110.087-128.269 28.278-49.49 47.47-104.027 56.56-159.578 5.050-23.23-2.020-47.47-18.18-66.659zM303.942 823.961c-5.050 8.079-13.13 14.141-22.22 16.16s-18.18-1.009-26.259-6.061c-39.391-31.31-72.72-70.7-97.969-114.127-25.249-44.441-42.42-91.908-50.499-142.407-1.009-9.090 1.009-18.18 7.069-25.249s15.149-11.109 25.249-11.109h241.387c6.061 30.299 21.21 56.56 44.441 75.748l-121.197 207.047zM347.373 179.591c-5.050-8.079-6.061-18.18-3.028-27.27 3.028-9.090 10.1-16.16 18.18-19.188 47.47-18.18 96.958-28.278 148.468-28.278s100.998 9.090 148.468 28.278c9.090 3.028 15.149 10.1 18.18 19.188 3.028 9.090 2.020 19.188-3.028 27.27l-118.167 208.058c-14.141-5.050-28.278-7.069-44.441-7.069s-30.299 3.028-44.441 7.069l-120.189-208.058zM512 598.735c-46.46 0-83.829-37.37-83.829-83.829s37.37-83.829 83.829-83.829 83.829 37.37 83.829 83.829-37.37 83.829-83.829 83.829zM917.006 576.516c-8.079 50.499-24.238 97.969-50.499 142.407-25.249 44.441-58.579 82.818-97.969 114.127-7.069 6.061-16.16 8.079-26.259 6.061-9.090-2.020-18.18-8.079-22.22-16.16l-120.189-207.047c22.22-19.188 38.379-46.46 44.441-75.748h240.377c10.1 0 19.188 4.041 25.249 11.109 5.050 7.069 8.079 16.16 7.069 25.249z" p-id="12530"></path>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="icon-platform" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M199.68 564.906667L170.666667 721.92h250.88L392.533333 564.906667H199.68zM179.2 512h235.52c11.946667 0 23.893333 8.533333 25.6 22.186667l39.253333 209.92c1.706667 6.826667 0 15.36-5.12 22.186666s-11.946667 10.24-20.48 10.24H139.946667c-8.533333 0-15.36-3.413333-20.48-10.24-5.12-6.826667-6.826667-13.653333-5.12-22.186666L153.6 534.186667c1.706667-13.653333 11.946667-22.186667 25.6-22.186667z m411.306667 209.92h250.88l-29.013334-157.013333H619.52l-29.013333 157.013333zM597.333333 512h235.52c11.946667 0 23.893333 8.533333 25.6 22.186667l39.253334 209.92c1.706667 6.826667 0 15.36-5.12 22.186666s-11.946667 10.24-20.48 10.24H558.08c-8.533333 0-15.36-3.413333-20.48-10.24-5.12-6.826667-6.826667-13.653333-5.12-22.186666l39.253333-209.92c3.413333-13.653333 13.653333-22.186667 25.6-22.186667z m-216.746666-52.906667H631.466667l-29.013334-157.013333H409.6l-29.013333 157.013333z m6.826666-209.92h235.52c11.946667 0 23.893333 8.533333 25.6 22.186667l39.253334 209.92c1.706667 6.826667 0 15.36-5.12 22.186667s-11.946667 10.24-20.48 10.24H348.16c-8.533333 0-15.36-3.413333-20.48-10.24-5.12-6.826667-6.826667-13.653333-5.12-22.186667l39.253333-209.92c3.413333-11.946667 13.653333-22.186667 25.6-22.186667z m0 0" p-id="9166"></path>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="icon-station" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M890.3 390.6l-309.1-299c-38.3-37-100.5-37.1-138.9-0.3l-308.5 296c-9.2 8.9-14.4 20.9-14.4 33.4v456c0 52.2 43.9 84.4 98.1 84.4h147.2c54.2 0 98.1-32.3 98.1-84.4v-47.2c0-26.1 22-47.2 49.1-47.2s49.1 21.2 49.1 47.2v47.2c0 52.2 43.9 84.4 98.1 84.4h147.2c54.2 0 98.1-32.3 98.1-84.4V424c0.2-12.5-5-24.5-14.1-33.4z m-83.9 438.9c0 26.1-22 37.2-49.1 37.2h-49.1c-27.1 0-49.1-11.1-49.1-37.2v-47.2c0-52.2-43.9-104.5-98.1-104.5h-98.1c-54.2 0-98.1 52.3-98.1 104.5v47.2c0 26.1-22 37.2-49.1 37.2h-49.1c-27.1 0-49.1-11.1-49.1-37.2V448.7c0-6.3 2.6-12.3 7.2-16.7l252.1-242.6c19.2-18.5 50.3-18.5 69.4 0L799.3 433c4.6 4.4 7.2 10.4 7.2 16.7v379.8z" p-id="20574"></path>
|
||||||
|
</symbol>
|
||||||
|
<!-- <symbol id="icon-signal" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M63.33 895.64V127.99h64v767.65z" fill="#fdfafa" p-id="24054"></path><path d="M750 365.57a146.25 146.25 0 1 1-103.36 42.83A145.29 145.29 0 0 1 750 365.57m0-64c-116.12 0-210.25 94.13-210.25 210.25S633.93 722.06 750 722.06s210.3-94.13 210.3-210.25S866.16 301.57 750 301.57z" fill="#fdfafa" p-id="24055"></path><path d="M336.94 365.57a146 146 0 1 1-103.27 42.78 145.09 145.09 0 0 1 103.27-42.78m0-64c-116 0-210 94-210 210s94 210 210 210 210-94 210-210-94-210-210-210z" fill="#fdfafa" p-id="24056"></path>
|
||||||
|
</symbol> -->
|
||||||
|
<symbol id="icon-signal" viewBox="0 0 1024 1024">
|
||||||
|
<path d="M63.33 895.64V127.99h64v767.65z" fill="#ffffff" p-id="1554"></path><path d="M750 365.57a146.25 146.25 0 1 1-103.36 42.83A145.29 145.29 0 0 1 750 365.57m0-64c-116.12 0-210.25 94.13-210.25 210.25S633.93 722.06 750 722.06s210.3-94.13 210.3-210.25S866.16 301.57 750 301.57z" fill="#ffffff" p-id="1555"></path><path d="M336.94 365.57a146 146 0 1 1-103.27 42.78 145.09 145.09 0 0 1 103.27-42.78m0-64c-116 0-210 94-210 210s94 210 210 210 210-94 210-210-94-210-210-210z" fill="#ffffff" p-id="1556"></path>
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.9 KiB |
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
public/icons/favicon-128x128.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
public/icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 859 B |
BIN
public/icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
public/icons/favicon-96x96.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
213
quasar.config.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
|
||||||
|
* the ES6 features that are supported by your Node version. https://node.green/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Configuration for your app
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
|
||||||
|
|
||||||
|
const { configure } = require('quasar/wrappers');
|
||||||
|
|
||||||
|
const BasePath = 'bjrtss';
|
||||||
|
|
||||||
|
module.exports = configure(function (/* ctx */) {
|
||||||
|
return {
|
||||||
|
eslint: {
|
||||||
|
// fix: true,
|
||||||
|
// include: [],
|
||||||
|
// exclude: [],
|
||||||
|
// rawOptions: {},
|
||||||
|
warnings: true,
|
||||||
|
errors: true,
|
||||||
|
exclude: ['src/protos/*'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
|
||||||
|
// preFetch: true,
|
||||||
|
|
||||||
|
// app boot file (/src/boot)
|
||||||
|
// --> boot files are part of "main.js"
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||||
|
boot: ['axios', '@pixi/graphics-extras'],
|
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||||
|
css: ['app.scss'],
|
||||||
|
|
||||||
|
// https://github.com/quasarframework/quasar/tree/dev/extras
|
||||||
|
extras: [
|
||||||
|
// 'ionicons-v4',
|
||||||
|
// 'mdi-v5',
|
||||||
|
// 'fontawesome-v6',
|
||||||
|
// 'eva-icons',
|
||||||
|
// 'themify',
|
||||||
|
// 'line-awesome',
|
||||||
|
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
|
||||||
|
|
||||||
|
'roboto-font', // optional, you are not bound to it
|
||||||
|
'material-icons', // optional, you are not bound to it
|
||||||
|
'material-symbols-outlined',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
|
||||||
|
build: {
|
||||||
|
target: {
|
||||||
|
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
|
||||||
|
node: 'node16',
|
||||||
|
},
|
||||||
|
|
||||||
|
vueRouterMode: 'history', // available values: 'hash', 'history'
|
||||||
|
vueRouterBase: BasePath,
|
||||||
|
// vueDevtools,
|
||||||
|
// vueOptionsAPI: false,
|
||||||
|
|
||||||
|
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
|
||||||
|
|
||||||
|
publicPath: BasePath,
|
||||||
|
// analyze: true,
|
||||||
|
// env: {},
|
||||||
|
// rawDefine: {}
|
||||||
|
// ignorePublicFolder: true,
|
||||||
|
// minify: false,
|
||||||
|
// polyfillModulePreload: true,
|
||||||
|
// distDir
|
||||||
|
// distDir: `dist/${BasePath}`,
|
||||||
|
|
||||||
|
// extendViteConf (viteConf) {},
|
||||||
|
// viteVuePluginOptions: {},
|
||||||
|
|
||||||
|
// vitePlugins: [
|
||||||
|
// [ 'package-name', { ..options.. } ]
|
||||||
|
// ]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
|
||||||
|
devServer: {
|
||||||
|
// https: true
|
||||||
|
open: true, // opens browser window automatically
|
||||||
|
base: BasePath,
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
|
||||||
|
framework: {
|
||||||
|
config: {
|
||||||
|
notify: {
|
||||||
|
position: 'top',
|
||||||
|
timeout: 2000,
|
||||||
|
progress: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// iconSet: 'material-icons', // Quasar icon set
|
||||||
|
// lang: 'en-US', // Quasar language pack
|
||||||
|
lang: 'zh-CN',
|
||||||
|
|
||||||
|
// For special cases outside of where the auto-import strategy can have an impact
|
||||||
|
// (like functional components as one of the examples),
|
||||||
|
// you can manually specify Quasar components/directives to be available everywhere:
|
||||||
|
//
|
||||||
|
// components: [],
|
||||||
|
// directives: [],
|
||||||
|
|
||||||
|
// Quasar plugins
|
||||||
|
plugins: ['Notify', 'Dialog', 'Dark', 'AppFullscreen', 'Loading'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// animations: 'all', // --- includes all animations
|
||||||
|
// https://v2.quasar.dev/options/animations
|
||||||
|
animations: [],
|
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles
|
||||||
|
// sourceFiles: {
|
||||||
|
// rootComponent: 'src/App.vue',
|
||||||
|
// router: 'src/router/index',
|
||||||
|
// store: 'src/store/index',
|
||||||
|
// registerServiceWorker: 'src-pwa/register-service-worker',
|
||||||
|
// serviceWorker: 'src-pwa/custom-service-worker',
|
||||||
|
// pwaManifestFile: 'src-pwa/manifest.json',
|
||||||
|
// electronMain: 'src-electron/electron-main',
|
||||||
|
// electronPreload: 'src-electron/electron-preload'
|
||||||
|
// },
|
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
|
||||||
|
ssr: {
|
||||||
|
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
|
||||||
|
// will mess up SSR
|
||||||
|
|
||||||
|
// extendSSRWebserverConf (esbuildConf) {},
|
||||||
|
// extendPackageJson (json) {},
|
||||||
|
|
||||||
|
pwa: false,
|
||||||
|
|
||||||
|
// manualStoreHydration: true,
|
||||||
|
// manualPostHydrationTrigger: true,
|
||||||
|
|
||||||
|
prodPort: 3000, // The default port that the production server should use
|
||||||
|
// (gets superseded if process.env.PORT is specified at runtime)
|
||||||
|
|
||||||
|
middlewares: [
|
||||||
|
'render', // keep this as last one
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
|
||||||
|
pwa: {
|
||||||
|
workboxMode: 'generateSW', // or 'injectManifest'
|
||||||
|
injectPwaMetaTags: true,
|
||||||
|
swFilename: 'sw.js',
|
||||||
|
manifestFilename: 'manifest.json',
|
||||||
|
useCredentialsForManifestTag: false,
|
||||||
|
// useFilenameHashes: true,
|
||||||
|
// extendGenerateSWOptions (cfg) {}
|
||||||
|
// extendInjectManifestOptions (cfg) {},
|
||||||
|
// extendManifestJson (json) {}
|
||||||
|
// extendPWACustomSWConf (esbuildConf) {}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
|
||||||
|
cordova: {
|
||||||
|
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
|
||||||
|
},
|
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
|
||||||
|
capacitor: {
|
||||||
|
hideSplashscreen: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
|
||||||
|
electron: {
|
||||||
|
// extendElectronMainConf (esbuildConf)
|
||||||
|
// extendElectronPreloadConf (esbuildConf)
|
||||||
|
|
||||||
|
inspectPort: 5858,
|
||||||
|
|
||||||
|
bundler: 'packager', // 'packager' or 'builder'
|
||||||
|
|
||||||
|
packager: {
|
||||||
|
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
|
||||||
|
// OS X / Mac App Store
|
||||||
|
// appBundleId: '',
|
||||||
|
// appCategoryType: '',
|
||||||
|
// osxSign: '',
|
||||||
|
// protocol: 'myapp://path',
|
||||||
|
// Windows only
|
||||||
|
// win32metadata: { ... }
|
||||||
|
},
|
||||||
|
|
||||||
|
builder: {
|
||||||
|
// https://www.electron.build/configuration/configuration
|
||||||
|
|
||||||
|
appId: 'bj-rtss-client',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
|
||||||
|
bex: {
|
||||||
|
contentScripts: ['my-content-script'],
|
||||||
|
|
||||||
|
// extendBexScriptsConf (esbuildConf) {}
|
||||||
|
// extendBexManifestJson (json) {}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
91
scripts/proto.cjs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* 将proto文件编译到 src/proto/
|
||||||
|
*/
|
||||||
|
const { readdirSync } = require('fs');
|
||||||
|
const { resolve } = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
|
const messageDir = resolve(__dirname, '../bj-rtss-message');
|
||||||
|
const protoDir = resolve(messageDir, 'protos');
|
||||||
|
const destDir = resolve(__dirname, '../src/protos');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归处理所有proto文件生成
|
||||||
|
* @param {*} file 文件
|
||||||
|
* @param {*} path 目录
|
||||||
|
*/
|
||||||
|
function recursiveGenerate(file, path = [], cmds = []) {
|
||||||
|
if (file.isFile()) {
|
||||||
|
// 文件,生成
|
||||||
|
if (file.name.endsWith('.proto')) {
|
||||||
|
cmds.push(buildGenerateCmd(file.name, path));
|
||||||
|
} else {
|
||||||
|
console.warn('不是proto文件', file.name);
|
||||||
|
}
|
||||||
|
} else if (file.isDirectory()) {
|
||||||
|
// 文件夹,递归
|
||||||
|
readdirSync(resolve(protoDir, ...path, file.name), {
|
||||||
|
withFileTypes: true,
|
||||||
|
}).forEach((f) => {
|
||||||
|
const subPath = [...path, file.name];
|
||||||
|
recursiveGenerate(f, subPath, cmds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLinux = os.type().toLowerCase().includes('linux');
|
||||||
|
|
||||||
|
function buildGenerateCmd(name, path = []) {
|
||||||
|
const protoPath = resolve(protoDir, ...path);
|
||||||
|
const tsPath = resolve(destDir, ...path);
|
||||||
|
|
||||||
|
let cmd = ['protoc', `-I=${protoPath}`, `--ts_out=${tsPath}`, `${name}`];
|
||||||
|
let cmdStr = cmd.join(' ');
|
||||||
|
return cmdStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const protocDir = resolve(messageDir, 'protoc-23.1');
|
||||||
|
const protocBin = resolve(
|
||||||
|
protocDir,
|
||||||
|
`bin/${isLinux ? 'linux-x86_64' : 'win64'}`
|
||||||
|
);
|
||||||
|
const prepareCmds = [];
|
||||||
|
const setPathCmd = isLinux
|
||||||
|
? ['export', `PATH=${protocBin}:${protocDir}:"$PATH"`].join(' ')
|
||||||
|
: ['set', `PATH=${protocBin};${protocDir};%PATH%`].join(' ');
|
||||||
|
const protocVersionCmd = ['protoc', '--version'].join(' ');
|
||||||
|
prepareCmds.push(setPathCmd);
|
||||||
|
prepareCmds.push(protocVersionCmd);
|
||||||
|
|
||||||
|
readdirSync(protoDir, {
|
||||||
|
withFileTypes: true,
|
||||||
|
}).forEach((f) => {
|
||||||
|
recursiveGenerate(f, [], prepareCmds);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(prepareCmds);
|
||||||
|
|
||||||
|
exec(
|
||||||
|
prepareCmds.join(' && '),
|
||||||
|
{
|
||||||
|
maxBuffer: 1024 * 2000,
|
||||||
|
},
|
||||||
|
(err, stdout, stderr) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw err;
|
||||||
|
} else if (stderr.length > 0) {
|
||||||
|
console.error(stderr.toString());
|
||||||
|
throw new Error(stderr.toString());
|
||||||
|
} else {
|
||||||
|
console.log(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行
|
||||||
|
main();
|
70
scripts/sync.cjs
Normal 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();
|
7
src/App.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
42
src/api/ApiCommon.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export class PageQueryDto {
|
||||||
|
current: number;
|
||||||
|
size: number;
|
||||||
|
orders?: OrderItemDto[];
|
||||||
|
constructor(current: number, size: number, orders?: OrderItemDto[]) {
|
||||||
|
this.current = current;
|
||||||
|
this.size = size;
|
||||||
|
this.orders = orders;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OrderItemDto {
|
||||||
|
column: string;
|
||||||
|
asc: boolean;
|
||||||
|
constructor(column: string, asc: boolean) {
|
||||||
|
this.column = column;
|
||||||
|
this.asc = asc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static asc(column: string): OrderItemDto {
|
||||||
|
return new OrderItemDto(column, true);
|
||||||
|
}
|
||||||
|
static desc(column: string): OrderItemDto {
|
||||||
|
return new OrderItemDto(column, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageDto<T = unknown> {
|
||||||
|
records: T[];
|
||||||
|
/**
|
||||||
|
* 记录总数
|
||||||
|
*/
|
||||||
|
total: number;
|
||||||
|
/**
|
||||||
|
* 第几页
|
||||||
|
*/
|
||||||
|
current: number;
|
||||||
|
/**
|
||||||
|
* 每页数量
|
||||||
|
*/
|
||||||
|
size: number;
|
||||||
|
}
|
86
src/api/DraftApi.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { api } from 'src/boot/axios';
|
||||||
|
import { PageDto, PageQueryDto } from './ApiCommon';
|
||||||
|
|
||||||
|
const DraftUriBase = '/api/drafting';
|
||||||
|
|
||||||
|
interface Item {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
proto: string;
|
||||||
|
type: string;
|
||||||
|
createdAt: string;
|
||||||
|
updateAt: string;
|
||||||
|
creatorId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PagingQueryParams extends PageQueryDto {
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function pageQuery(
|
||||||
|
params: PagingQueryParams
|
||||||
|
): Promise<PageDto<Item>> {
|
||||||
|
const response = await api.get(`${DraftUriBase}/paging`, {
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建草稿
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function createDraft(draftData: { name: string; type: string }) {
|
||||||
|
return api.post(`${DraftUriBase}`, draftData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除草稿
|
||||||
|
* @param id 草稿id
|
||||||
|
*/
|
||||||
|
export function deleteDraft(id: number) {
|
||||||
|
return api.delete(`${DraftUriBase}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取草稿数据
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getDraft(id: number): Promise<Item> {
|
||||||
|
const response = await api.get(`${DraftUriBase}/${id}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存草稿数据
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function saveDraft(
|
||||||
|
id: number,
|
||||||
|
data: {
|
||||||
|
proto: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return api.put(`${DraftUriBase}/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 另存草稿数据
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function saveAsDraft(
|
||||||
|
id: number,
|
||||||
|
data: { name: string; proto: string }
|
||||||
|
): Promise<Item> {
|
||||||
|
const response = await api.post(`${DraftUriBase}/${id}/saveAs`, data);
|
||||||
|
return response.data;
|
||||||
|
}
|
79
src/api/LineInfoApi.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { api } from 'src/boot/axios';
|
||||||
|
import { PageDto, PageQueryDto } from './ApiCommon';
|
||||||
|
|
||||||
|
const UriBase = '/api/lineInfo';
|
||||||
|
|
||||||
|
export interface createParams {
|
||||||
|
name: string;
|
||||||
|
lineId: number;
|
||||||
|
config?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Item extends createParams {
|
||||||
|
id: number;
|
||||||
|
createdAt: string;
|
||||||
|
updateAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PagingQueryParams extends PageQueryDto {
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function pageQuery(
|
||||||
|
params: PagingQueryParams
|
||||||
|
): Promise<PageDto<Item>> {
|
||||||
|
const response = await api.get(`${UriBase}/paging`, {
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建线路
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function createLine(data: createParams) {
|
||||||
|
return api.post(`${UriBase}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除线路
|
||||||
|
* @param id 线路id
|
||||||
|
*/
|
||||||
|
export function deleteLine(id: number) {
|
||||||
|
return api.delete(`${UriBase}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存线路数据
|
||||||
|
* @param id 草稿id
|
||||||
|
*/
|
||||||
|
export function saveLineData(id: number, data: createParams) {
|
||||||
|
return api.put(`${UriBase}/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取线路数据详情
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getLineInfo(id: number): Promise<Item> {
|
||||||
|
const response = await api.get(`${UriBase}/${id}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取线路信息列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getLineList(): Promise<Array<Item>> {
|
||||||
|
const response = await api.get(`${UriBase}/list`);
|
||||||
|
return response.data;
|
||||||
|
}
|
89
src/api/PublishApi.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { api } from 'src/boot/axios';
|
||||||
|
import { PageDto, PageQueryDto } from './ApiCommon';
|
||||||
|
|
||||||
|
const PublishUriBase = '/api/publishedGi';
|
||||||
|
|
||||||
|
interface Item {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
proto: string;
|
||||||
|
createdAt: string;
|
||||||
|
updateAt: string;
|
||||||
|
creatorId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PagingQueryParams extends PageQueryDto {
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 草稿图发布
|
||||||
|
* @param id 草稿id
|
||||||
|
*/
|
||||||
|
export function publishDraft(data: {
|
||||||
|
name: string;
|
||||||
|
lineId?: number;
|
||||||
|
draftingId: number;
|
||||||
|
}) {
|
||||||
|
return api.post(`${PublishUriBase}/publish`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取发布图形数据列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getDraft(): Promise<Item> {
|
||||||
|
const response = await api.get(`${PublishUriBase}/list`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function pageQuery(
|
||||||
|
params: PagingQueryParams
|
||||||
|
): Promise<PageDto<Item>> {
|
||||||
|
const response = await api.get(`${PublishUriBase}/paging`, {
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除发布图
|
||||||
|
* @param id 草稿id
|
||||||
|
*/
|
||||||
|
export function deletePublish(id: number) {
|
||||||
|
return api.delete(`${PublishUriBase}/${id}`);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取发布地图详细信息
|
||||||
|
* @param id 发布地图id
|
||||||
|
*/
|
||||||
|
export async function getPublishMapInfoById(id: number): Promise<Item> {
|
||||||
|
const response = await api.get(`${PublishUriBase}/${id}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取已发布的线路地图数据
|
||||||
|
*/
|
||||||
|
export async function getPublishLineNet(): Promise<Item> {
|
||||||
|
const response = await api.get(`${PublishUriBase}/publish/lineNetwork/info`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取发布地图详细信息
|
||||||
|
* @param id 发布地图线路ID
|
||||||
|
* @param type 发布地图线路类型
|
||||||
|
*/
|
||||||
|
export async function getPublishMapInfoByLineId(
|
||||||
|
lineId: number,
|
||||||
|
type: string
|
||||||
|
): Promise<Item> {
|
||||||
|
const response = await api.get(`${PublishUriBase}/${type}/${lineId}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
77
src/api/UserApi.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { api } from 'src/boot/axios';
|
||||||
|
import { PageDto, PageQueryDto } from './ApiCommon';
|
||||||
|
import { Md5 } from 'ts-md5';
|
||||||
|
|
||||||
|
const UserUriBase = '/api/user';
|
||||||
|
|
||||||
|
interface RegisterInfo {
|
||||||
|
name: string;
|
||||||
|
mobile: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
mobile: string;
|
||||||
|
password: string;
|
||||||
|
registerTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PasswordSult = '4a6d74126bfd06d69406fcccb7e7d5d9'; // 密码加盐
|
||||||
|
function encryptPassword(password: string): string {
|
||||||
|
const md5 = new Md5();
|
||||||
|
return md5.appendStr(`${password}${PasswordSult}`).end() as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
* @param info
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function register(info: RegisterInfo): Promise<User> {
|
||||||
|
const response = await api.post(`${UserUriBase}/register`, {
|
||||||
|
...info,
|
||||||
|
password: encryptPassword(info.password),
|
||||||
|
});
|
||||||
|
return response.data as User;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoginInfo {
|
||||||
|
account: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
* @param loginInfo
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function login(loginInfo: LoginInfo): Promise<string> {
|
||||||
|
const info = { ...loginInfo, password: encryptPassword(loginInfo.password) };
|
||||||
|
const response = await api.post(`${UserUriBase}/login`, info);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PagingQueryParams extends PageQueryDto {
|
||||||
|
name?: string;
|
||||||
|
roleId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询用户信息
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function pageQuery(
|
||||||
|
params: PagingQueryParams
|
||||||
|
): Promise<PageDto<User>> {
|
||||||
|
const response = await api.get(`${UserUriBase}/paging`, {
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function distributeRole(query: { userId: number; roleIds: number[] }) {
|
||||||
|
return api.post('/api/role/distribute', query);
|
||||||
|
}
|
15
src/assets/quasar-logo-vertical.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
|
||||||
|
<path
|
||||||
|
d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
|
||||||
|
<path fill="#050A14"
|
||||||
|
d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
|
||||||
|
<path fill="#00B4FF"
|
||||||
|
d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
|
||||||
|
<path fill="#00B4FF"
|
||||||
|
d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
|
||||||
|
<path fill="#050A14"
|
||||||
|
d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
|
||||||
|
<path fill="#00B4FF"
|
||||||
|
d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
|
||||||
|
<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.4 KiB |
0
src/boot/.gitkeep
Normal file
7
src/boot/@pixi/graphics-extras.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { boot } from 'quasar/wrappers';
|
||||||
|
import * as GraphicsExtras from '@pixi/graphics-extras';
|
||||||
|
// "async" is optional;
|
||||||
|
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
|
||||||
|
export default boot(async (/* { app, router, ... } */) => {
|
||||||
|
GraphicsExtras;
|
||||||
|
});
|
111
src/boot/axios.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import axios, { AxiosInstance } from 'axios';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { Dialog } from 'quasar';
|
||||||
|
import { boot } from 'quasar/wrappers';
|
||||||
|
import { getJwtToken } from 'src/configs/TokenManage';
|
||||||
|
import { getHttpBase } from 'src/configs/UrlManage';
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
$axios: AxiosInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorData {
|
||||||
|
status: number;
|
||||||
|
title: string;
|
||||||
|
detail: string;
|
||||||
|
code: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApiError {
|
||||||
|
origin: AxiosError;
|
||||||
|
/**
|
||||||
|
* 业务错误代码
|
||||||
|
*/
|
||||||
|
code: number;
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
/**
|
||||||
|
* 相关问题描述
|
||||||
|
*/
|
||||||
|
detail?: string;
|
||||||
|
constructor(origin: AxiosError<unknown, unknown>) {
|
||||||
|
this.origin = origin;
|
||||||
|
const response = origin.response;
|
||||||
|
if (response) {
|
||||||
|
const err = response.data as ErrorData;
|
||||||
|
this.code = err.code;
|
||||||
|
this.title = err.title;
|
||||||
|
this.detail = err.detail;
|
||||||
|
} else {
|
||||||
|
this.code = origin.status || -1;
|
||||||
|
this.title = origin.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static from(err: AxiosError<unknown, unknown>): ApiError {
|
||||||
|
return new ApiError(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否认证失败(登录过期)
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
isAuthError(): boolean {
|
||||||
|
return this.origin.response?.status === 401;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Be careful when using SSR for cross-request state pollution
|
||||||
|
// due to creating a Singleton instance here;
|
||||||
|
// If any client changes this (global) instance, it might be a
|
||||||
|
// good idea to move this instance creation inside of the
|
||||||
|
// "export default () => {}" function below (which runs individually
|
||||||
|
// for each client)
|
||||||
|
const api = axios.create({ baseURL: getHttpBase() });
|
||||||
|
|
||||||
|
export default boot(({ app, router }) => {
|
||||||
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||||
|
|
||||||
|
// 拦截请求,添加
|
||||||
|
api.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
config.headers.Authorization = getJwtToken();
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(err: AxiosError) => {
|
||||||
|
return Promise.reject(ApiError.from(err));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
if (err.response && err.response.status === 401) {
|
||||||
|
Dialog.create({
|
||||||
|
title: '认证失败',
|
||||||
|
message: '认证失败或登录超时,请重新登录',
|
||||||
|
persistent: true,
|
||||||
|
}).onOk(() => {
|
||||||
|
router.push({ name: 'login' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.reject(ApiError.from(err));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.config.globalProperties.$axios = axios;
|
||||||
|
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||||
|
// so you won't necessarily have to import axios in each vue file
|
||||||
|
|
||||||
|
app.config.globalProperties.$api = api;
|
||||||
|
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||||
|
// so you can easily perform requests against your app's API
|
||||||
|
});
|
||||||
|
|
||||||
|
export { api };
|
85
src/components/SysMenu.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<q-list bordered separator>
|
||||||
|
<div v-for="(menu, ii) in list" :key="ii">
|
||||||
|
<div v-if="menu.children">
|
||||||
|
<q-expansion-item :icon="menu.icon" :label="menu.label">
|
||||||
|
<q-card>
|
||||||
|
<q-list bordered separator>
|
||||||
|
<q-item
|
||||||
|
v-for="(item, index) in menu.children"
|
||||||
|
:key="index"
|
||||||
|
clickable
|
||||||
|
:to="item.path"
|
||||||
|
exact
|
||||||
|
:inset-level="1"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon :name="item.icon"></q-icon>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{ item.label }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<q-item clickable :to="menu.path" exact>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon :name="menu.icon"></q-icon>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{ menu.label }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
|
const list = reactive([
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
label: '系统管理',
|
||||||
|
icon: 'dataset',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/sysManage/user',
|
||||||
|
label: '用户管理',
|
||||||
|
icon: 'manage_accounts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
label: '数据管理',
|
||||||
|
icon: 'list_alt',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/dataManage/draft',
|
||||||
|
label: '草稿管理',
|
||||||
|
icon: 'app_registration',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dataManage/publish',
|
||||||
|
label: '发布管理',
|
||||||
|
icon: 'app_registration',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dataManage/lineInfo',
|
||||||
|
label: '线路信息管理',
|
||||||
|
icon: 'app_registration',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/monitor',
|
||||||
|
label: '监控',
|
||||||
|
icon: 'computer',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
</script>
|
133
src/components/draw-app/DrawProperties.vue
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 绘制图形模板属性 -->
|
||||||
|
<div v-if="drawStore.drawMode">
|
||||||
|
<q-card flat>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">{{ drawStore.drawGraphicName + ' 模板' }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-separator inset></q-separator>
|
||||||
|
<q-card-section>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Link.Type">
|
||||||
|
<link-template></link-template>
|
||||||
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Rect.Type">
|
||||||
|
<rect-template></rect-template>
|
||||||
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Platform.Type">
|
||||||
|
<platform-template></platform-template>
|
||||||
|
</template>
|
||||||
|
<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>
|
||||||
|
<!-- 画布或图形对象属性 -->
|
||||||
|
<div v-else-if="drawStore.selectedGraphics !== null">
|
||||||
|
<q-card flat>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">{{ drawStore.selectedObjName + ' 属性' }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-separator inset></q-separator>
|
||||||
|
<template v-if="drawStore.selectedGraphics.length === 0">
|
||||||
|
<q-card-section>
|
||||||
|
<canvas-property></canvas-property>
|
||||||
|
</q-card-section>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="drawStore.selectedGraphics.length === 1">
|
||||||
|
<q-card-section>
|
||||||
|
<link-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Link.Type"
|
||||||
|
></link-property>
|
||||||
|
<rect-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Rect.Type"
|
||||||
|
></rect-property>
|
||||||
|
<platform-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Platform.Type"
|
||||||
|
></platform-property>
|
||||||
|
<station-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Station.Type"
|
||||||
|
></station-property>
|
||||||
|
<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>
|
||||||
|
<signal-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === Signal.Type"
|
||||||
|
></signal-property>
|
||||||
|
<TurnoutProperty
|
||||||
|
v-else-if="drawStore.selectedGraphicType === Turnout.Type"
|
||||||
|
></TurnoutProperty>
|
||||||
|
<SectionProperty
|
||||||
|
v-else-if="drawStore.selectedGraphicType === Section.Type"
|
||||||
|
></SectionProperty>
|
||||||
|
<run-line-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === RunLine.Type"
|
||||||
|
></run-line-property>
|
||||||
|
<train-window-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === TrainWindow.Type"
|
||||||
|
></train-window-property>
|
||||||
|
<path-line-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === PathLine.Type"
|
||||||
|
></path-line-property>
|
||||||
|
<axle-counting-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === AxleCounting.Type"
|
||||||
|
></axle-counting-property>
|
||||||
|
<separator-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === Separator.Type"
|
||||||
|
></separator-property>
|
||||||
|
</q-card-section>
|
||||||
|
</template>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import LinkTemplate from './templates/LinkTemplate.vue';
|
||||||
|
import RectTemplate from './templates/RectTemplate.vue';
|
||||||
|
import PlatformTemplate from './templates/PlatformTemplate.vue';
|
||||||
|
import StationTemplate from './templates/StationTemplate.vue';
|
||||||
|
// import TrainTemplate from './templates/TrainTemplate.vue';
|
||||||
|
import CanvasProperty from './properties/CanvasProperty.vue';
|
||||||
|
import LinkProperty from './properties/LinkProperty.vue';
|
||||||
|
import RectProperty from './properties/RectProperty.vue';
|
||||||
|
import PlatformProperty from './properties/PlatformProperty.vue';
|
||||||
|
import StationProperty from './properties/StationProperty.vue';
|
||||||
|
import StationLineProperty from './properties/StationLineProperty.vue';
|
||||||
|
// import TrainProperty from './properties/TrainProperty.vue';
|
||||||
|
import TrainWindowProperty from './properties/TrainWindowProperty.vue';
|
||||||
|
import AxleCountingProperty from './properties/AxleCountingProperty.vue';
|
||||||
|
import IscsFanProperty from './properties/IscsFanProperty.vue';
|
||||||
|
import SignalProperty from './properties/SignalProperty.vue';
|
||||||
|
import TurnoutProperty from './properties/TurnoutProperty.vue';
|
||||||
|
import SectionProperty from './properties/SectionProperty.vue';
|
||||||
|
import RunLineProperty from './properties/RunLineProperty.vue';
|
||||||
|
import PathLineProperty from './properties/PathLineProperty.vue';
|
||||||
|
import SeparatorProperty from './properties/SeparatorProperty.vue';
|
||||||
|
import { Link } from 'src/graphics/link/Link';
|
||||||
|
import { Rect } from 'src/graphics/rect/Rect';
|
||||||
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
|
import { Station } from 'src/graphics/station/Station';
|
||||||
|
import { StationLine } from 'src/graphics/stationLine/StationLine';
|
||||||
|
// import { Train } from 'src/graphics/train/Train';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { Signal } from 'src/graphics/signal/Signal';
|
||||||
|
import { Turnout } from 'src/graphics/turnout/Turnout';
|
||||||
|
import { RunLine } from 'src/graphics/runLine/RunLine';
|
||||||
|
import { Section } from 'src/graphics/section/Section';
|
||||||
|
import { TrainWindow } from 'src/graphics/trainWindow/TrainWindow';
|
||||||
|
import { PathLine } from 'src/graphics/pathLine/PathLine';
|
||||||
|
import { AxleCounting } from 'src/graphics/axleCounting/AxleCounting';
|
||||||
|
import { Separator } from 'src/graphics/separator/Separator';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
</script>
|
164
src/components/draw-app/properties/AxleCountingProperty.vue
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<template>
|
||||||
|
<q-form class="q-gutter-sm">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
readonly
|
||||||
|
v-model="axleCountingModel.id"
|
||||||
|
label="id"
|
||||||
|
hint=""
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
label="计轴名称"
|
||||||
|
type="textarea"
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="axleCountingModel.code"
|
||||||
|
lazy-rules
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
<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">
|
||||||
|
<q-item-label> 关联的区段 </q-item-label>
|
||||||
|
<div class="q-gutter-sm row">
|
||||||
|
<q-chip
|
||||||
|
v-for="item in sectionRelations"
|
||||||
|
:key="item"
|
||||||
|
square
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</q-chip>
|
||||||
|
</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
|
||||||
|
v-for="item in turnoutRelations"
|
||||||
|
:key="item"
|
||||||
|
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 { 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, onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const axleCountingModel = reactive(new AxleCountingData());
|
||||||
|
const kilometerSystem = reactive({ coordinateSystem: '', kilometer: 0 });
|
||||||
|
|
||||||
|
const CoordinateSystemOptions = [
|
||||||
|
{ label: '车辆段', value: 'DEPOT' },
|
||||||
|
{ label: '停车场', value: 'PARKING_LOT' },
|
||||||
|
{ label: '正线', value: 'MAIN_LINE' },
|
||||||
|
{ 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 =
|
||||||
|
axleCounting?.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
axleCounting,
|
||||||
|
Section.Type
|
||||||
|
);
|
||||||
|
const ref = sectionRelations.map(
|
||||||
|
(relation) =>
|
||||||
|
`${relation.getOtherGraphic<Section>(axleCounting).datas.code}(${
|
||||||
|
relation.getOtherRelationParam(axleCounting).param
|
||||||
|
})`
|
||||||
|
);
|
||||||
|
return Array.from(new Set(ref));
|
||||||
|
});
|
||||||
|
|
||||||
|
const turnoutRelations = computed(() => {
|
||||||
|
const axleCounting = drawStore.selectedGraphic as AxleCounting;
|
||||||
|
const turnoutRelations =
|
||||||
|
axleCounting?.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
axleCounting,
|
||||||
|
Turnout.Type
|
||||||
|
);
|
||||||
|
const ref = turnoutRelations.map(
|
||||||
|
(relation) =>
|
||||||
|
`${relation.getOtherGraphic<Turnout>(axleCounting).datas.code}(${
|
||||||
|
relation.getOtherRelationParam(axleCounting).param
|
||||||
|
})`
|
||||||
|
);
|
||||||
|
return Array.from(new Set(ref));
|
||||||
|
});
|
||||||
|
</script>
|
80
src/components/draw-app/properties/CanvasProperty.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="canvas.width"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="画布宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
type="number"
|
||||||
|
v-model.number="canvas.height"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="画布高 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => val > 0 || '画布高必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="canvas.backgroundColor"
|
||||||
|
@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
|
||||||
|
:model-value="canvas.backgroundColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
canvas.backgroundColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, onUnmounted, reactive } from 'vue';
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
|
||||||
|
const canvas = reactive({
|
||||||
|
width: 1920,
|
||||||
|
height: 1080,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('画布属性表单mounted');
|
||||||
|
const jc = drawStore.getJlCanvas();
|
||||||
|
canvas.width = jc.properties.width;
|
||||||
|
canvas.height = jc.properties.height;
|
||||||
|
canvas.backgroundColor = jc.properties.backgroundColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
console.log('画布属性表单unmounted');
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
console.log('画布属性更新');
|
||||||
|
const app = drawStore.getDrawApp();
|
||||||
|
app.updateCanvasAndRecord({
|
||||||
|
...canvas,
|
||||||
|
viewportTransform: app.canvas.properties.viewportTransform,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
42
src/components/draw-app/properties/IscsFanProperty.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="model.id" label="id" :rules="[]" />
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { IscsFanData } from 'src/drawApp/graphics/IscsFanInteraction';
|
||||||
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const model = reactive(new IscsFanData());
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == IscsFan.Type) {
|
||||||
|
// console.log('Iscs风机变更');
|
||||||
|
model.copyFrom(val.saveData() as IscsFanData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// console.log('Iscs风机 属性表单 mounted');
|
||||||
|
const g = drawStore.selectedGraphic as IscsFan;
|
||||||
|
if (g) {
|
||||||
|
model.copyFrom(g.saveData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
console.log('Iscs风机 属性更新');
|
||||||
|
const g = drawStore.selectedGraphic as IscsFan;
|
||||||
|
if (g) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(g, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
96
src/components/draw-app/properties/LinkProperty.vue
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="linkModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="linkModel.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="linkModel.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="linkModel.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
linkModel.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<!-- <q-btn-toggle
|
||||||
|
disable
|
||||||
|
v-model="linkModel.curve"
|
||||||
|
:options="[
|
||||||
|
{ label: '直线', value: false },
|
||||||
|
{ label: '曲线', value: true },
|
||||||
|
]"
|
||||||
|
/> -->
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-if="linkModel.curve"
|
||||||
|
outlined
|
||||||
|
v-model.number="linkModel.segmentsCount"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="曲线分段数量"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '曲线分段数量必须大于0']"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
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 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>
|
91
src/components/draw-app/properties/PathLineProperty.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="pathLineModel.id" label="id" hint="" />
|
||||||
|
<q-checkbox
|
||||||
|
v-model="pathLineModel.isUp"
|
||||||
|
label="是否上行"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
style="margin-top: 10px"
|
||||||
|
v-model="pathLineModel.code"
|
||||||
|
:options="lineList"
|
||||||
|
:map-options="true"
|
||||||
|
:emit-value="true"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
label="关联线路"
|
||||||
|
></q-select>
|
||||||
|
<template :key="item" v-for="item in pathLineModel.kilometerPoints">
|
||||||
|
<!-- <div style="display: flex; margin-top: 5px">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="item.point.x"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
:label="`${item.stName}x:`"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="item.point.y"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
:label="`${item.stName}y:`"
|
||||||
|
/>
|
||||||
|
</div> -->
|
||||||
|
<div style="display: flex; margin-top: 5px">
|
||||||
|
<q-input outlined readonly v-model="item.stName" label="车站" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="item.kilometer"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
:label="`${item.stName}公里标:`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PathLineData } from 'src/drawApp/graphics/PathLineInteraction';
|
||||||
|
import { PathLine } from 'src/graphics/pathLine/PathLine';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { reactive, onMounted, watch } from 'vue';
|
||||||
|
import { getLineList } from 'src/api/LineInfoApi';
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
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) => {
|
||||||
|
res.forEach((item) => {
|
||||||
|
lineList.push({ value: item.lineId + '', label: item.name });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.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>
|
75
src/components/draw-app/properties/PlatformProperty.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="platformModel.id" label="id" hint="" />
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasDoor"
|
||||||
|
:options="optionsDoor"
|
||||||
|
label="是否有屏蔽门"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="direction"
|
||||||
|
:options="optionsDirection"
|
||||||
|
label="方向"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PlatformData } from 'src/drawApp/graphics/PlatformInteraction';
|
||||||
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const platformModel = reactive(new PlatformData());
|
||||||
|
const hasDoor = ref('是');
|
||||||
|
const optionsDoor = ['是', '否'];
|
||||||
|
const direction = ref('向上');
|
||||||
|
const optionsDirection = ['向上', '向下'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
向上 = 'up',
|
||||||
|
向下 = 'down',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
up = '向上',
|
||||||
|
down = '向下',
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
platformModel.hasdoor = JSON.parse((showSelect as never)[hasDoor.value]);
|
||||||
|
platformModel.direction = (showSelect as never)[direction.value];
|
||||||
|
const platform = drawStore.selectedGraphic as Platform;
|
||||||
|
if (platform) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(platform, platformModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
100
src/components/draw-app/properties/RectProperty.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="rectModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="rectModel.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="rectModel.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="rectModel.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
rectModel.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="rectModel.width"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="宽度"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '宽度必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="rectModel.height"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="高度"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '宽度必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="rectModel.radius"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="圆角半径"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '圆角半径必须大于0']"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
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 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>
|
189
src/components/draw-app/properties/RunLineProperty.vue
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="runLineModel.id" label="id" hint="" />
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
style="margin-top: 10px"
|
||||||
|
v-model="runLineModel.lineId"
|
||||||
|
:options="lineList"
|
||||||
|
:map-options="true"
|
||||||
|
:emit-value="true"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
label="关联线路"
|
||||||
|
>
|
||||||
|
</q-select>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
style="margin-top: 10px"
|
||||||
|
v-model="runLineModel.code"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="名称"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="runLineModel.nameColor"
|
||||||
|
style="margin-top: 10px"
|
||||||
|
@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.nameColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
runLineModel.nameColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="runLineModel.nameBgColor"
|
||||||
|
@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.nameBgColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
runLineModel.nameBgColor = 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
|
||||||
|
outlined
|
||||||
|
v-model.number="item.x"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
:label="`x${index + 1}:`"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="item.y"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
:label="`y${index + 1}:`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
style="margin-top: 10px"
|
||||||
|
v-model="runLineModel.containSta"
|
||||||
|
:options="stationLines"
|
||||||
|
option-value="id"
|
||||||
|
option-label="code"
|
||||||
|
:multiple="true"
|
||||||
|
:map-options="true"
|
||||||
|
:emit-value="true"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
label="关联车站"
|
||||||
|
>
|
||||||
|
<template v-slot:after>
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
label="生成"
|
||||||
|
@click="generateContainSta"
|
||||||
|
/> </template
|
||||||
|
></q-select>
|
||||||
|
<div style="margin-top: 10px; display: flex; justify-content: center">
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
style="margin: auto"
|
||||||
|
@click="generatePathLine"
|
||||||
|
label="生成轨迹线"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { RunLineData } from 'src/drawApp/graphics/RunLineInteraction';
|
||||||
|
import { RunLine } from 'src/graphics/runLine/RunLine';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch, ref } from 'vue';
|
||||||
|
import { Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
IStationLineData,
|
||||||
|
StationLine,
|
||||||
|
} from 'src/graphics/stationLine/StationLine';
|
||||||
|
import { getLineList } from 'src/api/LineInfoApi';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
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) => {
|
||||||
|
res.forEach((item) => {
|
||||||
|
lineList.push({ value: item.lineId + '', label: item.name });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.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[] = [];
|
||||||
|
points.forEach((p) => points1.push(new Point(p.x, p.y)));
|
||||||
|
runLine.generatePathLine(points1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function generateContainSta() {
|
||||||
|
const runLine = drawStore.selectedGraphic as RunLine;
|
||||||
|
if (runLine) {
|
||||||
|
runLine.generateContainSta();
|
||||||
|
runLineModel.copyFrom(runLine.saveData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
132
src/components/draw-app/properties/SectionProperty.vue
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="sectionModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="sectionModel.code"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="编号"
|
||||||
|
/>
|
||||||
|
<q-field class="q-mt-lg" outlined label="关联区段" readonly stack-label>
|
||||||
|
<template #control>
|
||||||
|
<q-chip
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
v-for="code in sectionRelations"
|
||||||
|
:key="code"
|
||||||
|
square
|
||||||
|
>{{ code }}</q-chip
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</q-field>
|
||||||
|
<q-field class="q-mt-lg" outlined label="关联道岔" readonly stack-label>
|
||||||
|
<template #control>
|
||||||
|
<q-chip
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
v-for="code in turnoutRelations"
|
||||||
|
:key="code"
|
||||||
|
square
|
||||||
|
>{{ code }}</q-chip
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</q-field>
|
||||||
|
<q-input class="q-mt-lg" outlined v-model="splitNum" type="number">
|
||||||
|
<template #after>
|
||||||
|
<q-btn
|
||||||
|
:disable="sectionModel.data.sectionType !== SectionType.Physical"
|
||||||
|
color="primary"
|
||||||
|
@click="splitSection"
|
||||||
|
>拆分为逻辑区段</q-btn
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SectionData } from 'src/drawApp/graphics/SectionInteraction';
|
||||||
|
import { Section, SectionType } from 'src/graphics/section/Section';
|
||||||
|
import { Turnout } from 'src/graphics/turnout/Turnout';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { computed, ref, shallowRef, toRaw, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
|
||||||
|
const sectionModel = shallowRef(new SectionData());
|
||||||
|
|
||||||
|
const splitNum = ref(3);
|
||||||
|
|
||||||
|
function splitSection() {
|
||||||
|
const sectionData = toRaw(sectionModel.value);
|
||||||
|
const section = toRaw(drawStore.selectedGraphic as Section);
|
||||||
|
const app = drawStore.getDrawApp();
|
||||||
|
const points = section.getSplitPoints(splitNum.value);
|
||||||
|
const childIds: string[] = [];
|
||||||
|
|
||||||
|
points.forEach((ps, i) => {
|
||||||
|
const data = new SectionData();
|
||||||
|
data.points = ps.map((p) => new graphicData.Point({ x: p.x, y: p.y }));
|
||||||
|
data.id = app.drawAssistants
|
||||||
|
.find((as) => as.name === Section.name)!
|
||||||
|
.nextId();
|
||||||
|
data.code = `${sectionData.code}-${'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.charAt(
|
||||||
|
i % 26
|
||||||
|
)}`;
|
||||||
|
data.sectionType = SectionType.Logic;
|
||||||
|
const section = app.graphicTemplateMap.get(Section.name)!.new();
|
||||||
|
section?.loadData(data);
|
||||||
|
app.addGraphics(section);
|
||||||
|
childIds.push(data.id);
|
||||||
|
});
|
||||||
|
sectionData.children = childIds;
|
||||||
|
app.updateGraphicAndRecord(section, sectionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sectionRelations = computed(() => {
|
||||||
|
const section = drawStore.selectedGraphic as Section;
|
||||||
|
|
||||||
|
const sectionRelations =
|
||||||
|
section?.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
section,
|
||||||
|
Section.Type
|
||||||
|
);
|
||||||
|
return sectionRelations.map(
|
||||||
|
(relation) =>
|
||||||
|
`${relation.getRelationParam(section).param}: ${
|
||||||
|
relation.getOtherGraphic<Section>(section).datas.code
|
||||||
|
}(${relation.getOtherRelationParam(section).param})`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const turnoutRelations = computed(() => {
|
||||||
|
const section = drawStore.selectedGraphic as Section;
|
||||||
|
|
||||||
|
const turnoutRelations =
|
||||||
|
section?.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
section,
|
||||||
|
Turnout.Type
|
||||||
|
);
|
||||||
|
return turnoutRelations.map(
|
||||||
|
(relation) =>
|
||||||
|
`${relation.getRelationParam(section).param}: ${
|
||||||
|
relation.getOtherGraphic<Turnout>(section).datas.code
|
||||||
|
}(${relation.getOtherRelationParam(section).param})`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
57
src/components/draw-app/properties/SeparatorProperty.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="separatorModel.id" label="id" hint="" />
|
||||||
|
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
style="margin-top: 10px"
|
||||||
|
v-model="separatorModel.separatorType"
|
||||||
|
:options="typeOptions"
|
||||||
|
:map-options="true"
|
||||||
|
:emit-value="true"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
label="分隔符类型"
|
||||||
|
></q-select>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SeparatorData } from 'src/drawApp/graphics/SeparatorInteraction';
|
||||||
|
import { Separator } from 'src/graphics/separator/Separator';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const separatorModel = reactive(new SeparatorData());
|
||||||
|
|
||||||
|
const typeOptions = [
|
||||||
|
{ label: '区段分隔符', value: 'section' },
|
||||||
|
{ label: '道岔分隔符', value: 'turnout' },
|
||||||
|
{ label: '左断路分隔符', value: 'endA' },
|
||||||
|
{ label: '右断路分隔符', value: '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>
|
85
src/components/draw-app/properties/SignalProperty.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="signalModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="signalModel.code"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="编号"
|
||||||
|
/>
|
||||||
|
<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-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SignalData } from 'src/drawApp/graphics/SignalInteraction';
|
||||||
|
import { Signal } from 'src/graphics/signal/Signal';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const signalModel = reactive(new SignalData());
|
||||||
|
const kilometerSystem = reactive({ coordinateSystem: '', kilometer: 0 });
|
||||||
|
|
||||||
|
const CoordinateSystemOptions = [
|
||||||
|
{ label: '车辆段', value: 'DEPOT' },
|
||||||
|
{ label: '停车场', value: 'PARKING_LOT' },
|
||||||
|
{ label: '正线', value: 'MAIN_LINE' },
|
||||||
|
{ label: '换线', value: 'TRANSFER' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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 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>
|
87
src/components/draw-app/properties/StationLineProperty.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
readonly
|
||||||
|
v-model="stationLineModel.id"
|
||||||
|
label="id"
|
||||||
|
hint=""
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
label="车站名称"
|
||||||
|
type="textarea"
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="stationLineModel.code"
|
||||||
|
lazy-rules
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="stationLineModel.hideName"
|
||||||
|
label="名称是否隐藏"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasTransfer"
|
||||||
|
:options="optionsCircle"
|
||||||
|
label="是否有换乘"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { StationLineData } from 'src/drawApp/graphics/StationLineInteraction';
|
||||||
|
import { StationLine } from 'src/graphics/stationLine/StationLine';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const stationLineModel = reactive(new StationLineData());
|
||||||
|
const hasTransfer = ref('是');
|
||||||
|
const optionsCircle = ['是', '否'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
127
src/components/draw-app/properties/StationProperty.vue
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<q-form class="q-gutter-sm">
|
||||||
|
<q-input outlined readonly v-model="stationModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
label="车站名称"
|
||||||
|
type="textarea"
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="stationModel.code"
|
||||||
|
lazy-rules
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
<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-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasControl"
|
||||||
|
:options="optionsControl"
|
||||||
|
label="是否有控制"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="concentrationStations"
|
||||||
|
:options="optionsControl"
|
||||||
|
label="是否集中站"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { StationData } from 'src/drawApp/graphics/StationInteraction';
|
||||||
|
import { Station } from 'src/graphics/station/Station';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const stationModel = reactive(new StationData());
|
||||||
|
const 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' },
|
||||||
|
{ label: '停车场', value: 'PARKING_LOT' },
|
||||||
|
{ label: '正线', value: 'MAIN_LINE' },
|
||||||
|
{ label: '换线', value: 'TRANSFER' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
84
src/components/draw-app/properties/TrainProperty.vue
Normal 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>
|
86
src/components/draw-app/properties/TrainWindowProperty.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<q-form class="q-gutter-sm">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
readonly
|
||||||
|
v-model="trainWindowModel.id"
|
||||||
|
label="id"
|
||||||
|
hint=""
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
label="车次窗名称"
|
||||||
|
type="textarea"
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="trainWindowModel.code"
|
||||||
|
lazy-rules
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
|
||||||
|
<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 relatedProcessPositions"
|
||||||
|
:key="item.id"
|
||||||
|
square
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
>
|
||||||
|
{{ item.datas.code }}
|
||||||
|
</q-chip>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TrainWindowData } from 'src/drawApp/graphics/TrainWindowInteraction';
|
||||||
|
import { Section } from 'src/graphics/section/Section';
|
||||||
|
import { TrainWindow } from 'src/graphics/trainWindow/TrainWindow';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { computed, onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
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 relatedProcessPositions = computed((): Section[] => {
|
||||||
|
if (
|
||||||
|
drawStore.selectedGraphic &&
|
||||||
|
drawStore.selectedGraphic.type === 'TrainWindow'
|
||||||
|
) {
|
||||||
|
return (drawStore.selectedGraphic as TrainWindow).getRelatedSections();
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
</script>
|
149
src/components/draw-app/properties/TurnoutProperty.vue
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="turnoutModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="turnoutModel.code"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="编号"
|
||||||
|
/>
|
||||||
|
<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
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
v-for="code in sectionRelations"
|
||||||
|
:key="code"
|
||||||
|
square
|
||||||
|
>{{ code }}</q-chip
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</q-field>
|
||||||
|
<q-field class="q-mt-lg" outlined label="关联道岔" readonly stack-label>
|
||||||
|
<template #control>
|
||||||
|
<q-chip
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
v-for="code in turnoutRelations"
|
||||||
|
:key="code"
|
||||||
|
square
|
||||||
|
>{{ code }}</q-chip
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</q-field>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TurnoutData } from 'src/drawApp/graphics/TurnoutInteraction';
|
||||||
|
import { Section } from 'src/graphics/section/Section';
|
||||||
|
import { Turnout } from 'src/graphics/turnout/Turnout';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { computed, reactive, shallowRef, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
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;
|
||||||
|
|
||||||
|
const sectionRelations =
|
||||||
|
turnout.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
turnout,
|
||||||
|
Section.Type
|
||||||
|
);
|
||||||
|
return sectionRelations.map(
|
||||||
|
(relation) =>
|
||||||
|
`${relation.getRelationParam(turnout).param}: ${
|
||||||
|
relation.getOtherGraphic<Section>(turnout).datas.code
|
||||||
|
}(${relation.getOtherRelationParam(turnout).param})`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const turnoutRelations = computed(() => {
|
||||||
|
const turnout = drawStore.selectedGraphic as Turnout;
|
||||||
|
|
||||||
|
const turnoutRelations =
|
||||||
|
turnout?.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
turnout,
|
||||||
|
Turnout.Type
|
||||||
|
);
|
||||||
|
return turnoutRelations.map(
|
||||||
|
(relation) =>
|
||||||
|
`${relation.getRelationParam(turnout).param}: ${
|
||||||
|
relation.getOtherGraphic<Turnout>(turnout).datas.code
|
||||||
|
}(${relation.getOtherRelationParam(turnout).param})`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
96
src/components/draw-app/templates/LinkTemplate.vue
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线色 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<q-btn-toggle
|
||||||
|
v-model="template.curve"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
:options="[
|
||||||
|
{ label: '直线', value: false },
|
||||||
|
{ label: '曲线', value: true },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-if="template.curve"
|
||||||
|
outlined
|
||||||
|
v-model.number="template.segmentsCount"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="曲线分段数量 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '曲线分段数量必须大于0']"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
curve: false,
|
||||||
|
segmentsCount: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
template.curve = lt.curve;
|
||||||
|
template.segmentsCount = lt.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
gt.curve = template.curve;
|
||||||
|
gt.segmentsCount = template.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
76
src/components/draw-app/templates/PlatformTemplate.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线色 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
curve: false,
|
||||||
|
segmentsCount: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
template.curve = lt.curve;
|
||||||
|
template.segmentsCount = lt.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
gt.curve = template.curve;
|
||||||
|
gt.segmentsCount = template.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
76
src/components/draw-app/templates/RectTemplate.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线色 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
curve: false,
|
||||||
|
segmentsCount: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
template.curve = lt.curve;
|
||||||
|
template.segmentsCount = lt.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
gt.curve = template.curve;
|
||||||
|
gt.segmentsCount = template.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
70
src/components/draw-app/templates/StationTemplate.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体颜色 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
51
src/components/draw-app/templates/TrainTemplate.vue
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="template.hasBorder"
|
||||||
|
:options="optionsDoor"
|
||||||
|
label="是否有边框"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="template.trainDirection"
|
||||||
|
:options="optionsDirection"
|
||||||
|
label="行驶方向"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TrainTemplate } from 'src/graphics/train/Train';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
trainDirection: 'left',
|
||||||
|
hasBorder: true,
|
||||||
|
});
|
||||||
|
const optionsDoor = ['是', '否'];
|
||||||
|
const optionsDirection = ['向左', '向右'];
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as TrainTemplate;
|
||||||
|
template.trainDirection = lt.trainDirection;
|
||||||
|
template.hasBorder = lt.hasBorder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as TrainTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.trainDirection = template.trainDirection;
|
||||||
|
gt.hasBorder = template.hasBorder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
13
src/configs/TokenManage.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const JwtTokenKey = 'jwttoken';
|
||||||
|
|
||||||
|
export function saveJwtToken(token: string) {
|
||||||
|
sessionStorage.setItem(JwtTokenKey, `Bearer ${token}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getJwtToken(): string | null {
|
||||||
|
return sessionStorage.getItem(JwtTokenKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearJwtToken(): void {
|
||||||
|
sessionStorage.removeItem(JwtTokenKey);
|
||||||
|
}
|
15
src/configs/UrlManage.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
function getHost(): string {
|
||||||
|
// 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 `http://${getHost()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWebsocketUrl() {
|
||||||
|
return `ws://${getHost()}/ws-default`;
|
||||||
|
}
|
1
src/css/app.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
// app global css in SCSS form
|
25
src/css/quasar.variables.scss
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Quasar SCSS (& Sass) Variables
|
||||||
|
// --------------------------------------------------
|
||||||
|
// To customize the look and feel of this app, you can override
|
||||||
|
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
|
||||||
|
|
||||||
|
// Check documentation for full list of Quasar variables
|
||||||
|
|
||||||
|
// Your own variables (that are declared here) and Quasar's own
|
||||||
|
// ones will be available out of the box in your .vue/.scss/.sass files
|
||||||
|
|
||||||
|
// It's highly recommended to change the default colors
|
||||||
|
// to match your app's branding.
|
||||||
|
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||||
|
|
||||||
|
$primary : #1976D2;
|
||||||
|
$secondary : #26A69A;
|
||||||
|
$accent : #9C27B0;
|
||||||
|
|
||||||
|
$dark : #1D1D1D;
|
||||||
|
$dark-page : #121212;
|
||||||
|
|
||||||
|
$positive : #21BA45;
|
||||||
|
$negative : #C10015;
|
||||||
|
$info : #31CCEC;
|
||||||
|
$warning : #F2C037;
|
56
src/drawApp/graphics/AxleCountingInteraction.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
AxleCounting,
|
||||||
|
IAxleCountingData,
|
||||||
|
} from 'src/graphics/axleCounting/AxleCounting';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
import { KilometerSystem } from 'src/graphics/signal/Signal';
|
||||||
|
|
||||||
|
export class AxleCountingData
|
||||||
|
extends GraphicDataBase
|
||||||
|
implements IAxleCountingData
|
||||||
|
{
|
||||||
|
constructor(data?: graphicData.AxleCounting) {
|
||||||
|
let axleCounting;
|
||||||
|
if (!data) {
|
||||||
|
axleCounting = new graphicData.AxleCounting({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(AxleCounting.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
axleCounting = data;
|
||||||
|
}
|
||||||
|
super(axleCounting);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.AxleCounting {
|
||||||
|
return this.getData<graphicData.AxleCounting>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get kilometerSystem(): KilometerSystem {
|
||||||
|
return this.data.kilometerSystem;
|
||||||
|
}
|
||||||
|
set kilometerSystem(v: KilometerSystem) {
|
||||||
|
this.data.kilometerSystem = new graphicData.KilometerSystem(v);
|
||||||
|
}
|
||||||
|
get axleCountingRef(): graphicData.RelatedRef[] {
|
||||||
|
return this.data.axleCountingRef;
|
||||||
|
}
|
||||||
|
set axleCountingRef(points: graphicData.RelatedRef[]) {
|
||||||
|
this.data.axleCountingRef = points;
|
||||||
|
}
|
||||||
|
clone(): AxleCountingData {
|
||||||
|
return new AxleCountingData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: AxleCountingData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: AxleCountingData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
153
src/drawApp/graphics/GraphicDataBase.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
ChildTransform,
|
||||||
|
GraphicData,
|
||||||
|
GraphicState,
|
||||||
|
GraphicTransform,
|
||||||
|
IChildTransform,
|
||||||
|
IGraphicTransform,
|
||||||
|
} 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: string;
|
||||||
|
graphicType: string;
|
||||||
|
transform: IGraphicTransform;
|
||||||
|
childTransforms: IChildTransform[];
|
||||||
|
}
|
||||||
|
export function fromStoragePoint(p: graphicData.Point): Point {
|
||||||
|
return new Point(p.x, p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toStoragePoint(p: IPointData): graphicData.Point {
|
||||||
|
return new graphicData.Point({ x: p.x, y: p.y });
|
||||||
|
}
|
||||||
|
export function fromStorageTransfrom(
|
||||||
|
transfrom: graphicData.Transform
|
||||||
|
): GraphicTransform {
|
||||||
|
return new GraphicTransform(
|
||||||
|
fromStoragePoint(transfrom.position),
|
||||||
|
fromStoragePoint(transfrom.scale),
|
||||||
|
transfrom.rotation,
|
||||||
|
fromStoragePoint(transfrom.skew)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toStorageTransform(
|
||||||
|
transform: GraphicTransform
|
||||||
|
): graphicData.Transform {
|
||||||
|
return new graphicData.Transform({
|
||||||
|
position: toStoragePoint(transform.position),
|
||||||
|
scale: toStoragePoint(transform.scale),
|
||||||
|
rotation: transform.rotation,
|
||||||
|
skew: toStoragePoint(transform.skew),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IProtoGraphicData extends pb_1.Message {
|
||||||
|
common: ICommonInfo;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class GraphicDataBase implements GraphicData {
|
||||||
|
_data: IProtoGraphicData;
|
||||||
|
constructor(data: IProtoGraphicData) {
|
||||||
|
this._data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultCommonInfo(graphicType: string): graphicData.CommonInfo {
|
||||||
|
return new graphicData.CommonInfo({
|
||||||
|
id: '',
|
||||||
|
graphicType: graphicType,
|
||||||
|
transform: new graphicData.Transform({
|
||||||
|
position: new graphicData.Point({ x: 0, y: 0 }),
|
||||||
|
scale: new graphicData.Point({ x: 1, y: 1 }),
|
||||||
|
rotation: 0,
|
||||||
|
skew: new graphicData.Point({ x: 0, y: 0 }),
|
||||||
|
}),
|
||||||
|
childTransforms: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getData<D extends IProtoGraphicData>(): D {
|
||||||
|
return this._data as D;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string {
|
||||||
|
return this._data.common.id;
|
||||||
|
}
|
||||||
|
set id(v: string) {
|
||||||
|
this._data.common.id = v;
|
||||||
|
}
|
||||||
|
get graphicType(): string {
|
||||||
|
return this._data.common.graphicType;
|
||||||
|
}
|
||||||
|
set graphicType(v: string) {
|
||||||
|
this._data.common.graphicType = v;
|
||||||
|
}
|
||||||
|
get transform(): GraphicTransform {
|
||||||
|
return GraphicTransform.from(this._data.common.transform);
|
||||||
|
}
|
||||||
|
set transform(v: GraphicTransform) {
|
||||||
|
this._data.common.transform = toStorageTransform(v);
|
||||||
|
}
|
||||||
|
get childTransforms(): ChildTransform[] | undefined {
|
||||||
|
const cts: ChildTransform[] = [];
|
||||||
|
if (this._data.common.childTransforms) {
|
||||||
|
this._data.common.childTransforms.forEach((ct) => {
|
||||||
|
cts.push(ChildTransform.from(ct));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return cts;
|
||||||
|
}
|
||||||
|
set childTransforms(v: ChildTransform[] | undefined) {
|
||||||
|
if (v) {
|
||||||
|
const cts: graphicData.ChildTransform[] = [];
|
||||||
|
v.forEach((ct) =>
|
||||||
|
cts.push(
|
||||||
|
new graphicData.ChildTransform({
|
||||||
|
...ct,
|
||||||
|
transform: toStorageTransform(ct.transform),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this._data.common.childTransforms = cts;
|
||||||
|
} else {
|
||||||
|
this._data.common.childTransforms = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): GraphicData {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
copyFrom(gd: GraphicDataBase): void {
|
||||||
|
pb_1.Message.copyInto(gd._data, this._data);
|
||||||
|
}
|
||||||
|
eq(other: GraphicDataBase): boolean {
|
||||||
|
return pb_1.Message.equals(this._data, other._data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class GraphicStateBase implements GraphicState {
|
||||||
|
_graphicType: string;
|
||||||
|
_state: pb_1.Message;
|
||||||
|
constructor(state: pb_1.Message, graphicType: string) {
|
||||||
|
this._state = state;
|
||||||
|
this._graphicType = graphicType;
|
||||||
|
}
|
||||||
|
abstract get code(): string;
|
||||||
|
abstract copyFrom(data: GraphicState): void;
|
||||||
|
abstract eq(data: GraphicState): boolean;
|
||||||
|
getState<S extends pb_1.Message>(): S {
|
||||||
|
return this._state as S;
|
||||||
|
}
|
||||||
|
get graphicType(): string {
|
||||||
|
return this._graphicType;
|
||||||
|
}
|
||||||
|
clone(): GraphicState {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
}
|
38
src/drawApp/graphics/IscsFanInteraction.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IIscsFanData, IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class IscsFanData extends GraphicDataBase implements IIscsFanData {
|
||||||
|
constructor(data?: graphicData.IscsFan) {
|
||||||
|
let fan;
|
||||||
|
if (data) {
|
||||||
|
fan = data;
|
||||||
|
} else {
|
||||||
|
fan = new graphicData.IscsFan({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(IscsFan.Type),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super(fan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.IscsFan {
|
||||||
|
return this.getData<graphicData.IscsFan>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
clone(): IscsFanData {
|
||||||
|
return new IscsFanData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: IscsFanData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: IscsFanData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
182
src/drawApp/graphics/LinkInteraction.ts
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
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 {
|
||||||
|
GraphicApp,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import {
|
||||||
|
addWayPoint,
|
||||||
|
clearWayPoint,
|
||||||
|
getWaypointRangeIndex,
|
||||||
|
PolylineEditPlugin,
|
||||||
|
removeLineWayPoint,
|
||||||
|
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
|
||||||
|
export class LinkData extends GraphicDataBase implements ILinkData {
|
||||||
|
constructor(data?: graphicData.Link) {
|
||||||
|
let link;
|
||||||
|
if (!data) {
|
||||||
|
link = new graphicData.Link({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Link.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
link = data;
|
||||||
|
}
|
||||||
|
super(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Link {
|
||||||
|
return this.getData<graphicData.Link>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get curve(): boolean {
|
||||||
|
return this.data.curve;
|
||||||
|
}
|
||||||
|
set curve(v: boolean) {
|
||||||
|
this.data.curve = v;
|
||||||
|
}
|
||||||
|
get curveNumber(): number {
|
||||||
|
return this.data.curve ? 1 : 0;
|
||||||
|
}
|
||||||
|
set curveNumber(v: number) {
|
||||||
|
this.data.curve = v === 0 ? false : true;
|
||||||
|
}
|
||||||
|
get segmentsCount(): number {
|
||||||
|
return this.data.segmentsCount;
|
||||||
|
}
|
||||||
|
set segmentsCount(v: number) {
|
||||||
|
this.data.segmentsCount = 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 lineWidth(): number {
|
||||||
|
return this.data.lineWidth;
|
||||||
|
}
|
||||||
|
set lineWidth(v: number) {
|
||||||
|
this.data.lineWidth = v;
|
||||||
|
}
|
||||||
|
get lineColor(): string {
|
||||||
|
return this.data.lineColor;
|
||||||
|
}
|
||||||
|
set lineColor(v: string) {
|
||||||
|
this.data.lineColor = v;
|
||||||
|
}
|
||||||
|
clone(): LinkData {
|
||||||
|
return new LinkData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: LinkData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: LinkData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addWaypointConfig: MenuItemOptions = {
|
||||||
|
name: '添加路径点',
|
||||||
|
};
|
||||||
|
export const removeWaypointConfig: MenuItemOptions = {
|
||||||
|
name: '移除路径点',
|
||||||
|
};
|
||||||
|
export const clearWaypointsConfig: MenuItemOptions = {
|
||||||
|
name: '清除所有路径点',
|
||||||
|
};
|
||||||
|
|
||||||
|
const LinkEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '轨道编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [addWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const EpEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '轨道编辑菜单2',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [removeWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export class DrawLinkPlugin extends GraphicInteractionPlugin<Link> {
|
||||||
|
static Name = 'link_draw_right_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(DrawLinkPlugin.Name, app);
|
||||||
|
app.registerMenu(LinkEditMenu);
|
||||||
|
app.registerMenu(EpEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new DrawLinkPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Link[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type === Link.Type).map((g) => g as Link);
|
||||||
|
}
|
||||||
|
bind(g: Link): void {
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: Link): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject) {
|
||||||
|
const polylineEditPlugin = g.assistantAppendMap.get(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
) as PolylineEditPlugin;
|
||||||
|
const link = g.getGraphic() as Link;
|
||||||
|
polylineEditPlugin.editedPoints.forEach((ep, index) => {
|
||||||
|
ep.on('rightclick', (e: FederatedMouseEvent) => {
|
||||||
|
this.app.registerMenu(EpEditMenu);
|
||||||
|
removeWaypointConfig.handler = () => {
|
||||||
|
removeLineWayPoint(link, index);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(link, false);
|
||||||
|
};
|
||||||
|
EpEditMenu.open(e.global);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const link = target.getGraphic() as Link;
|
||||||
|
this.app.updateSelected(link);
|
||||||
|
|
||||||
|
addWaypointConfig.handler = () => {
|
||||||
|
const linePoints = link.linePoints;
|
||||||
|
const p = link.screenToLocalPoint(e.global);
|
||||||
|
const { start, end } = getWaypointRangeIndex(
|
||||||
|
linePoints,
|
||||||
|
false,
|
||||||
|
p,
|
||||||
|
link.datas.lineWidth
|
||||||
|
);
|
||||||
|
addWayPoint(link, false, start, end, p);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(link, false);
|
||||||
|
};
|
||||||
|
LinkEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
70
src/drawApp/graphics/PathLineInteraction.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
IPathLineData,
|
||||||
|
PathLine,
|
||||||
|
KilometerPoint,
|
||||||
|
} from 'src/graphics/pathLine/PathLine';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class PathLineData extends GraphicDataBase implements IPathLineData {
|
||||||
|
constructor(data?: graphicData.PathLine) {
|
||||||
|
let pathLine;
|
||||||
|
if (!data) {
|
||||||
|
pathLine = new graphicData.PathLine({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(PathLine.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
pathLine = data;
|
||||||
|
}
|
||||||
|
super(pathLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.PathLine {
|
||||||
|
return this.getData<graphicData.PathLine>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 isUp(): boolean {
|
||||||
|
return this.data.isUp;
|
||||||
|
}
|
||||||
|
set isUp(v: boolean) {
|
||||||
|
this.data.isUp = v;
|
||||||
|
}
|
||||||
|
get kilometerPoints(): KilometerPoint[] {
|
||||||
|
return this.data.kilometerPoints;
|
||||||
|
}
|
||||||
|
set kilometerPoints(kilometerPoints: KilometerPoint[]) {
|
||||||
|
this.data.kilometerPoints = kilometerPoints.map(
|
||||||
|
(p) =>
|
||||||
|
new graphicData.KilometerPoint({
|
||||||
|
point: new graphicData.Point({ x: p.point.x, y: p.point.y }),
|
||||||
|
kilometer: p.kilometer,
|
||||||
|
stName: p.stName,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
clone(): PathLineData {
|
||||||
|
return new PathLineData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: PathLineData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: PathLineData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
307
src/drawApp/graphics/PlatformInteraction.ts
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
IPlatformData,
|
||||||
|
IPlatformState,
|
||||||
|
Platform,
|
||||||
|
} from 'src/graphics/platform/Platform';
|
||||||
|
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 {
|
||||||
|
GraphicApp,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
|
||||||
|
|
||||||
|
export class PlatformData extends GraphicDataBase implements IPlatformData {
|
||||||
|
constructor(data?: graphicData.Platform) {
|
||||||
|
let platform;
|
||||||
|
if (!data) {
|
||||||
|
platform = new graphicData.Platform({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Platform.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
platform = data;
|
||||||
|
}
|
||||||
|
super(platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Platform {
|
||||||
|
return this.getData<graphicData.Platform>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get hasdoor(): boolean {
|
||||||
|
return this.data.hasdoor;
|
||||||
|
}
|
||||||
|
set hasdoor(v: boolean) {
|
||||||
|
this.data.hasdoor = v;
|
||||||
|
}
|
||||||
|
get direction(): string {
|
||||||
|
return this.data.direction;
|
||||||
|
}
|
||||||
|
set direction(v: string) {
|
||||||
|
this.data.direction = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): PlatformData {
|
||||||
|
return new PlatformData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: PlatformData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: PlatformData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlatformState extends GraphicStateBase implements IPlatformState {
|
||||||
|
constructor(proto?: state.Platform) {
|
||||||
|
let states;
|
||||||
|
if (proto) {
|
||||||
|
states = proto;
|
||||||
|
} else {
|
||||||
|
states = new state.Platform();
|
||||||
|
}
|
||||||
|
super(states, Platform.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
get emergstop(): boolean {
|
||||||
|
return this.states.emergstop;
|
||||||
|
}
|
||||||
|
set emergstop(v: boolean) {
|
||||||
|
this.states.emergstop = v;
|
||||||
|
}
|
||||||
|
get trainberth(): boolean {
|
||||||
|
return this.states.trainberth;
|
||||||
|
}
|
||||||
|
set trainberth(v: boolean) {
|
||||||
|
this.states.trainberth = v;
|
||||||
|
}
|
||||||
|
get close(): boolean {
|
||||||
|
return this.states.close;
|
||||||
|
}
|
||||||
|
set close(v: boolean) {
|
||||||
|
this.states.close = v;
|
||||||
|
}
|
||||||
|
get upHold(): boolean {
|
||||||
|
return this.states.upHold;
|
||||||
|
}
|
||||||
|
set upHold(v: boolean) {
|
||||||
|
this.states.upHold = v;
|
||||||
|
}
|
||||||
|
get downHold(): boolean {
|
||||||
|
return this.states.downHold;
|
||||||
|
}
|
||||||
|
set downHold(v: boolean) {
|
||||||
|
this.states.downHold = v;
|
||||||
|
}
|
||||||
|
get upOccHold(): boolean {
|
||||||
|
return this.states.upOccHold;
|
||||||
|
}
|
||||||
|
set upOccHold(v: boolean) {
|
||||||
|
this.states.upOccHold = v;
|
||||||
|
}
|
||||||
|
get downOccHold(): boolean {
|
||||||
|
return this.states.downOccHold;
|
||||||
|
}
|
||||||
|
set downOccHold(v: boolean) {
|
||||||
|
this.states.downOccHold = v;
|
||||||
|
}
|
||||||
|
get psdOpen(): boolean {
|
||||||
|
return this.states.psdOpen;
|
||||||
|
}
|
||||||
|
set psdOpen(v: boolean) {
|
||||||
|
this.states.psdOpen = v;
|
||||||
|
}
|
||||||
|
get psdCut(): boolean {
|
||||||
|
return this.states.psdCut;
|
||||||
|
}
|
||||||
|
set psdCut(v: boolean) {
|
||||||
|
this.states.psdCut = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
get upSkipstop(): boolean {
|
||||||
|
return this.states.upSkipstop;
|
||||||
|
}
|
||||||
|
set upSkipstop(v: boolean) {
|
||||||
|
this.states.upSkipstop = v;
|
||||||
|
}
|
||||||
|
get downSkipstop(): boolean {
|
||||||
|
return this.states.downSkipstop;
|
||||||
|
}
|
||||||
|
set downSkipstop(v: boolean) {
|
||||||
|
this.states.downSkipstop = v;
|
||||||
|
}
|
||||||
|
get upTrainSkipstop(): boolean {
|
||||||
|
return this.states.upTrainSkipstop;
|
||||||
|
}
|
||||||
|
set upTrainSkipstop(v: boolean) {
|
||||||
|
this.states.upTrainSkipstop = v;
|
||||||
|
}
|
||||||
|
get downTrainSkipstop(): boolean {
|
||||||
|
return this.states.downTrainSkipstop;
|
||||||
|
}
|
||||||
|
set downTrainSkipstop(v: boolean) {
|
||||||
|
this.states.downTrainSkipstop = v;
|
||||||
|
}
|
||||||
|
get nextSectionRunTime(): number {
|
||||||
|
return this.states.nextSectionRunTime;
|
||||||
|
}
|
||||||
|
set nextSectionRunTime(v: number) {
|
||||||
|
this.states.nextSectionRunTime = v;
|
||||||
|
}
|
||||||
|
get nextSectionRunLevel(): number {
|
||||||
|
return this.states.nextSectionRunLevel;
|
||||||
|
}
|
||||||
|
set nextSectionRunLevel(v: number) {
|
||||||
|
this.states.nextSectionRunLevel = v;
|
||||||
|
}
|
||||||
|
get stopTime(): number {
|
||||||
|
return this.states.stopTime;
|
||||||
|
}
|
||||||
|
set stopTime(v: number) {
|
||||||
|
this.states.stopTime = v;
|
||||||
|
}
|
||||||
|
get states(): state.Platform {
|
||||||
|
return this.getState<state.Platform>();
|
||||||
|
}
|
||||||
|
clone(): PlatformState {
|
||||||
|
return new PlatformState(this.states.cloneMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: [
|
||||||
|
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: GraphicApp) {
|
||||||
|
super(PlatformOperateInteraction.Name, app);
|
||||||
|
app.registerMenu(PlatformOperateMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new PlatformOperateInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Platform[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Platform.Type)
|
||||||
|
.map((g) => g as Platform);
|
||||||
|
}
|
||||||
|
bind(g: Platform): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.selectable = true;
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: Platform): void {
|
||||||
|
g.selectable = false;
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const platform = target.getGraphic() as Platform;
|
||||||
|
this.app.updateSelected(platform);
|
||||||
|
holdConfig.handler = () => {
|
||||||
|
platform.states.upHold = true;
|
||||||
|
platform.states.upOccHold = true;
|
||||||
|
platform.doRepaint();
|
||||||
|
};
|
||||||
|
removeHoldrConfig.handler = () => {
|
||||||
|
platform.states.upHold = false;
|
||||||
|
platform.states.upOccHold = false;
|
||||||
|
platform.doRepaint();
|
||||||
|
};
|
||||||
|
skipStopConfig.handler = () => {
|
||||||
|
platform.states.upSkipstop = true;
|
||||||
|
platform.doRepaint();
|
||||||
|
};
|
||||||
|
removeSkipStopConfig.handler = () => {
|
||||||
|
platform.states.upSkipstop = false;
|
||||||
|
platform.doRepaint();
|
||||||
|
};
|
||||||
|
|
||||||
|
PlatformOperateMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
197
src/drawApp/graphics/PolygonInteraction.ts
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPolygonData, Polygon } from 'src/graphics/polygon/Polygon';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
import { DisplayObject, FederatedMouseEvent, IPointData } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicApp,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
|
||||||
|
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
|
||||||
|
import {
|
||||||
|
PolylineEditPlugin,
|
||||||
|
clearWayPoint,
|
||||||
|
removeLineWayPoint,
|
||||||
|
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
import {
|
||||||
|
addPolygonSegmentingPoint,
|
||||||
|
getWayLineIndex,
|
||||||
|
} from 'src/graphics/polygon/PolygonUtils';
|
||||||
|
|
||||||
|
export class PolygonData extends GraphicDataBase implements IPolygonData {
|
||||||
|
constructor(data?: graphicData.Polygon) {
|
||||||
|
let polygon;
|
||||||
|
if (!data) {
|
||||||
|
polygon = new graphicData.Polygon({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Polygon.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
polygon = data;
|
||||||
|
}
|
||||||
|
super(polygon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Polygon {
|
||||||
|
return this.getData<graphicData.Polygon>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get lineWidth(): number {
|
||||||
|
return this.data.lineWidth;
|
||||||
|
}
|
||||||
|
set lineWidth(v: number) {
|
||||||
|
this.data.lineWidth = v;
|
||||||
|
}
|
||||||
|
get lineColor(): string {
|
||||||
|
return this.data.lineColor;
|
||||||
|
}
|
||||||
|
set lineColor(v: string) {
|
||||||
|
this.data.lineColor = v;
|
||||||
|
}
|
||||||
|
get points(): IPointData[] {
|
||||||
|
return this.data.points;
|
||||||
|
}
|
||||||
|
set points(points: IPointData[]) {
|
||||||
|
this.data.points = points.map(
|
||||||
|
(p) => new graphicData.Point({ x: p.x, y: p.y })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): PolygonData {
|
||||||
|
return new PolygonData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: PolygonData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: PolygonData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* polygon编辑菜单配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
const threeSegmentingConfig: MenuItemOptions = {
|
||||||
|
name: '3等分',
|
||||||
|
};
|
||||||
|
const foreSegmentingConfig: MenuItemOptions = {
|
||||||
|
name: '4等分',
|
||||||
|
};
|
||||||
|
const fiveSegmentingConfig: MenuItemOptions = {
|
||||||
|
name: '5等分',
|
||||||
|
};
|
||||||
|
|
||||||
|
const chooseSegmentingConfig: MenuItemOptions = {
|
||||||
|
name: '细分',
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
name: '内置图形',
|
||||||
|
items: [
|
||||||
|
threeSegmentingConfig,
|
||||||
|
foreSegmentingConfig,
|
||||||
|
fiveSegmentingConfig,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const removeWaypointConfig: MenuItemOptions = {
|
||||||
|
name: '移除路径点',
|
||||||
|
};
|
||||||
|
const clearWaypointsConfig: MenuItemOptions = {
|
||||||
|
name: '清除所有路径点',
|
||||||
|
};
|
||||||
|
const PolygonEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '多边形边的编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [chooseSegmentingConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const EpEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '多边形点的编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [removeWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export class DrawPolygonPlugin extends GraphicInteractionPlugin<Polygon> {
|
||||||
|
static Name = 'polygon_draw_right_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(DrawPolygonPlugin.Name, app);
|
||||||
|
app.registerMenu(PolygonEditMenu);
|
||||||
|
app.registerMenu(EpEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new DrawPolygonPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Polygon[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Polygon.Type)
|
||||||
|
.map((g) => g as Polygon);
|
||||||
|
}
|
||||||
|
bind(g: Polygon): void {
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: Polygon): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject) {
|
||||||
|
const polylineEditPlugin = g.assistantAppendMap.get(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
) as PolylineEditPlugin;
|
||||||
|
const link = g.getGraphic() as Polygon;
|
||||||
|
polylineEditPlugin.editedPoints.forEach((ep, index) => {
|
||||||
|
ep.on('rightclick', (e: FederatedMouseEvent) => {
|
||||||
|
this.app.registerMenu(EpEditMenu);
|
||||||
|
removeWaypointConfig.handler = () => {
|
||||||
|
removeLineWayPoint(link, index);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(link, false);
|
||||||
|
};
|
||||||
|
EpEditMenu.open(e.global);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const polygon = target.getGraphic() as Polygon;
|
||||||
|
this.app.updateSelected(polygon);
|
||||||
|
const linePoints = polygon.addOnePoints();
|
||||||
|
const p = polygon.screenToLocalPoint(e.global);
|
||||||
|
const { start, end } = getWayLineIndex(linePoints, p);
|
||||||
|
|
||||||
|
chooseSegmentingConfig.handler = () => {
|
||||||
|
addPolygonSegmentingPoint(polygon, start, end);
|
||||||
|
};
|
||||||
|
threeSegmentingConfig.handler = () => {
|
||||||
|
addPolygonSegmentingPoint(polygon, start, end, 3);
|
||||||
|
};
|
||||||
|
foreSegmentingConfig.handler = () => {
|
||||||
|
addPolygonSegmentingPoint(polygon, start, end, 4);
|
||||||
|
};
|
||||||
|
fiveSegmentingConfig.handler = () => {
|
||||||
|
addPolygonSegmentingPoint(polygon, start, end, 5);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(polygon, false);
|
||||||
|
};
|
||||||
|
PolygonEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
75
src/drawApp/graphics/RectInteraction.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { IRectData, Rect } from 'src/graphics/rect/Rect';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class RectData extends GraphicDataBase implements IRectData {
|
||||||
|
constructor(data?: graphicData.Rect) {
|
||||||
|
let rect;
|
||||||
|
if (!data) {
|
||||||
|
rect = new graphicData.Rect({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Rect.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rect = data;
|
||||||
|
}
|
||||||
|
super(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Rect {
|
||||||
|
return this.getData<graphicData.Rect>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get lineWidth(): number {
|
||||||
|
return this.data.lineWidth;
|
||||||
|
}
|
||||||
|
set lineWidth(v: number) {
|
||||||
|
this.data.lineWidth = v;
|
||||||
|
}
|
||||||
|
get lineColor(): string {
|
||||||
|
return this.data.lineColor;
|
||||||
|
}
|
||||||
|
set lineColor(v: string) {
|
||||||
|
this.data.lineColor = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
get width(): number {
|
||||||
|
return this.data.width;
|
||||||
|
}
|
||||||
|
set width(v: number) {
|
||||||
|
this.data.width = v;
|
||||||
|
}
|
||||||
|
get height(): number {
|
||||||
|
return this.data.height;
|
||||||
|
}
|
||||||
|
set height(v: number) {
|
||||||
|
this.data.height = v;
|
||||||
|
}
|
||||||
|
get radius(): number {
|
||||||
|
return this.data.radius;
|
||||||
|
}
|
||||||
|
set radius(v: number) {
|
||||||
|
this.data.radius = v;
|
||||||
|
}
|
||||||
|
clone(): RectData {
|
||||||
|
return new RectData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: RectData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: RectData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
214
src/drawApp/graphics/RunLineInteraction.ts
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
IRunLineData,
|
||||||
|
RunLine,
|
||||||
|
runLineConsts,
|
||||||
|
} from 'src/graphics/runLine/RunLine';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
import {
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicApp,
|
||||||
|
JlGraphic,
|
||||||
|
} 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 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
import { RunLineGraphicHitArea } from 'src/graphics/runLine/RunLineDrawAssistant';
|
||||||
|
|
||||||
|
export class RunLineData extends GraphicDataBase implements IRunLineData {
|
||||||
|
constructor(data?: graphicData.RunLine) {
|
||||||
|
let runLine;
|
||||||
|
if (!data) {
|
||||||
|
runLine = new graphicData.RunLine({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(RunLine.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runLine = data;
|
||||||
|
}
|
||||||
|
super(runLine);
|
||||||
|
}
|
||||||
|
public get data(): graphicData.RunLine {
|
||||||
|
return this.getData<graphicData.RunLine>();
|
||||||
|
}
|
||||||
|
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 nameColor(): string {
|
||||||
|
return this.data.nameColor;
|
||||||
|
}
|
||||||
|
set nameColor(v: string) {
|
||||||
|
this.data.nameColor = v;
|
||||||
|
}
|
||||||
|
get nameBgColor(): string {
|
||||||
|
return this.data.nameBgColor;
|
||||||
|
}
|
||||||
|
set nameBgColor(v: string) {
|
||||||
|
this.data.nameBgColor = v;
|
||||||
|
}
|
||||||
|
get containSta(): string[] {
|
||||||
|
return this.data.containSta;
|
||||||
|
}
|
||||||
|
set containSta(v: string[]) {
|
||||||
|
this.data.containSta = v;
|
||||||
|
}
|
||||||
|
get linkPathLines(): string[] {
|
||||||
|
return this.data.linkPathLines;
|
||||||
|
}
|
||||||
|
set linkPathLines(v: string[]) {
|
||||||
|
this.data.linkPathLines = v;
|
||||||
|
}
|
||||||
|
get lineId(): string {
|
||||||
|
return this.data.lineId;
|
||||||
|
}
|
||||||
|
set lineId(v: string) {
|
||||||
|
this.data.lineId = v;
|
||||||
|
}
|
||||||
|
clone(): RunLineData {
|
||||||
|
return new RunLineData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: RunLineData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: RunLineData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addWaypointConfig: MenuItemOptions = {
|
||||||
|
name: '添加路径点',
|
||||||
|
};
|
||||||
|
export const removeWaypointConfig: MenuItemOptions = {
|
||||||
|
name: '移除路径点',
|
||||||
|
};
|
||||||
|
export const clearWaypointsConfig: MenuItemOptions = {
|
||||||
|
name: '清除所有路径点',
|
||||||
|
};
|
||||||
|
|
||||||
|
const RunLineEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '运行线编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [addWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const EpEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '运行线编辑菜单2',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [removeWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export class DrawRunLinePlugin extends GraphicInteractionPlugin<RunLine> {
|
||||||
|
static Name = 'runline_draw_right_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(DrawRunLinePlugin.Name, app);
|
||||||
|
app.registerMenu(RunLineEditMenu);
|
||||||
|
app.registerMenu(EpEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new DrawRunLinePlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): RunLine[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === RunLine.Type)
|
||||||
|
.map((g) => g as RunLine);
|
||||||
|
}
|
||||||
|
bind(g: RunLine): void {
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: RunLine): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject) {
|
||||||
|
const polylineEditPlugin = g.assistantAppendMap.get(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
) as PolylineEditPlugin;
|
||||||
|
const runLine = g.getGraphic() as RunLine;
|
||||||
|
polylineEditPlugin.editedPoints.forEach((ep, index) => {
|
||||||
|
ep.on('rightclick', (e: FederatedMouseEvent) => {
|
||||||
|
this.app.registerMenu(EpEditMenu);
|
||||||
|
removeWaypointConfig.handler = () => {
|
||||||
|
removeLineWayPoint(runLine, index);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(runLine, false);
|
||||||
|
};
|
||||||
|
EpEditMenu.open(e.global);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const runLine = target.getGraphic() as RunLine;
|
||||||
|
this.app.updateSelected(runLine);
|
||||||
|
|
||||||
|
addWaypointConfig.handler = () => {
|
||||||
|
const linePoints = runLine.linePoints;
|
||||||
|
const p = runLine.screenToLocalPoint(e.global);
|
||||||
|
const { start, end } = getWaypointRangeIndex(
|
||||||
|
linePoints,
|
||||||
|
false,
|
||||||
|
p,
|
||||||
|
runLineConsts.runLineWidth
|
||||||
|
);
|
||||||
|
addWayPoint(runLine, false, start, end, p);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(runLine, false);
|
||||||
|
};
|
||||||
|
RunLineEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RunLineOperateInteraction extends GraphicInteractionPlugin<RunLine> {
|
||||||
|
static Name = 'runLine_operate_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(RunLineOperateInteraction.Name, app);
|
||||||
|
app.registerMenu(EpEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new RunLineOperateInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): RunLine[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === RunLine.Type)
|
||||||
|
.map((g) => g as RunLine);
|
||||||
|
}
|
||||||
|
bind(g: RunLine): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.lineBody.hitArea = new RunLineGraphicHitArea(g);
|
||||||
|
g.selectable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: RunLine): void {
|
||||||
|
g.selectable = false;
|
||||||
|
g.eventMode = 'none';
|
||||||
|
}
|
||||||
|
}
|
69
src/drawApp/graphics/SectionInteraction.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
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 { IPointData } from 'pixi.js';
|
||||||
|
|
||||||
|
export class SectionData extends GraphicDataBase implements ISectionData {
|
||||||
|
constructor(data?: graphicData.Section) {
|
||||||
|
let section;
|
||||||
|
if (!data) {
|
||||||
|
section = new graphicData.Section({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Section.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
section = data;
|
||||||
|
}
|
||||||
|
super(section);
|
||||||
|
}
|
||||||
|
public get data(): graphicData.Section {
|
||||||
|
return this.getData<graphicData.Section>();
|
||||||
|
}
|
||||||
|
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 paRef(): graphicData.RelatedRef {
|
||||||
|
return this.data.paRef;
|
||||||
|
}
|
||||||
|
set paRef(ref: graphicData.RelatedRef) {
|
||||||
|
this.data.paRef = ref;
|
||||||
|
}
|
||||||
|
get pbRef(): graphicData.RelatedRef {
|
||||||
|
return this.data.pbRef;
|
||||||
|
}
|
||||||
|
set pbRef(ref: graphicData.RelatedRef) {
|
||||||
|
this.data.pbRef = ref;
|
||||||
|
}
|
||||||
|
get sectionType(): graphicData.Section.SectionType {
|
||||||
|
return this.data.sectionType;
|
||||||
|
}
|
||||||
|
set sectionType(type: graphicData.Section.SectionType) {
|
||||||
|
this.data.sectionType = type;
|
||||||
|
}
|
||||||
|
get children(): string[] {
|
||||||
|
return this.data.children;
|
||||||
|
}
|
||||||
|
set children(children: string[]) {
|
||||||
|
this.data.children = children;
|
||||||
|
}
|
||||||
|
clone(): SectionData {
|
||||||
|
return new SectionData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: SectionData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: SectionData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
43
src/drawApp/graphics/SeparatorInteraction.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { ISeparatorData, Separator } from 'src/graphics/separator/Separator';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class SeparatorData extends GraphicDataBase implements ISeparatorData {
|
||||||
|
constructor(data?: graphicData.Separator) {
|
||||||
|
let separator;
|
||||||
|
if (!data) {
|
||||||
|
separator = new graphicData.Separator({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Separator.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
separator = data;
|
||||||
|
}
|
||||||
|
super(separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Separator {
|
||||||
|
return this.getData<graphicData.Separator>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get separatorType(): string {
|
||||||
|
return this.data.separatorType;
|
||||||
|
}
|
||||||
|
set separatorType(v: string) {
|
||||||
|
this.data.separatorType = v;
|
||||||
|
}
|
||||||
|
clone(): SeparatorData {
|
||||||
|
return new SeparatorData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: SeparatorData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: SeparatorData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
369
src/drawApp/graphics/SignalInteraction.ts
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
ISignalData,
|
||||||
|
Signal,
|
||||||
|
ISignalState,
|
||||||
|
KilometerSystem,
|
||||||
|
} from 'src/graphics/signal/Signal';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
|
||||||
|
import {
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicApp,
|
||||||
|
JlGraphic,
|
||||||
|
} 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';
|
||||||
|
|
||||||
|
export class SignalData extends GraphicDataBase implements ISignalData {
|
||||||
|
constructor(data?: graphicData.Signal) {
|
||||||
|
let signal;
|
||||||
|
if (!data) {
|
||||||
|
signal = new graphicData.Signal({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Signal.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
signal = data;
|
||||||
|
}
|
||||||
|
super(signal);
|
||||||
|
}
|
||||||
|
public get data(): graphicData.Signal {
|
||||||
|
return this.getData<graphicData.Signal>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get mirror(): boolean {
|
||||||
|
return this.data.mirror;
|
||||||
|
}
|
||||||
|
set mirror(v: boolean) {
|
||||||
|
this.data.mirror = v;
|
||||||
|
}
|
||||||
|
get kilometerSystem(): KilometerSystem {
|
||||||
|
return this.data.kilometerSystem;
|
||||||
|
}
|
||||||
|
set kilometerSystem(v: KilometerSystem) {
|
||||||
|
this.data.kilometerSystem = new graphicData.KilometerSystem(v);
|
||||||
|
}
|
||||||
|
clone(): SignalData {
|
||||||
|
return new SignalData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: SignalData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: SignalData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SignalState extends GraphicStateBase implements ISignalState {
|
||||||
|
constructor(proto?: state.Signal) {
|
||||||
|
let states;
|
||||||
|
if (proto) {
|
||||||
|
states = proto;
|
||||||
|
} else {
|
||||||
|
states = new state.Signal();
|
||||||
|
}
|
||||||
|
super(states, Signal.Type);
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.states.id;
|
||||||
|
}
|
||||||
|
get redOpen(): boolean {
|
||||||
|
return this.states.redOpen;
|
||||||
|
}
|
||||||
|
set redOpen(v: boolean) {
|
||||||
|
this.states.redOpen = v;
|
||||||
|
}
|
||||||
|
get redFlash(): boolean {
|
||||||
|
return this.states.redFlash;
|
||||||
|
}
|
||||||
|
set redFlash(v: boolean) {
|
||||||
|
this.states.redFlash = v;
|
||||||
|
}
|
||||||
|
get greenOpen(): boolean {
|
||||||
|
return this.states.greenOpen;
|
||||||
|
}
|
||||||
|
set greenOpen(v: boolean) {
|
||||||
|
this.states.greenOpen = v;
|
||||||
|
}
|
||||||
|
get greenFlash(): boolean {
|
||||||
|
return this.states.greenFlash;
|
||||||
|
}
|
||||||
|
set greenFlash(v: boolean) {
|
||||||
|
this.states.greenFlash = v;
|
||||||
|
}
|
||||||
|
get yellowOpen(): boolean {
|
||||||
|
return this.states.yellowOpen;
|
||||||
|
}
|
||||||
|
set yellowOpen(v: boolean) {
|
||||||
|
this.states.yellowOpen = v;
|
||||||
|
}
|
||||||
|
get yellowFlash(): boolean {
|
||||||
|
return this.states.yellowFlash;
|
||||||
|
}
|
||||||
|
set yellowFlash(v: boolean) {
|
||||||
|
this.states.yellowFlash = v;
|
||||||
|
}
|
||||||
|
get whiteOpen(): boolean {
|
||||||
|
return this.states.whiteOpen;
|
||||||
|
}
|
||||||
|
set whiteOpen(v: boolean) {
|
||||||
|
this.states.whiteOpen = v;
|
||||||
|
}
|
||||||
|
get whiteFlash(): boolean {
|
||||||
|
return this.states.whiteFlash;
|
||||||
|
}
|
||||||
|
set whiteFlash(v: boolean) {
|
||||||
|
this.states.whiteFlash = v;
|
||||||
|
}
|
||||||
|
get blueOpen(): boolean {
|
||||||
|
return this.states.blueOpen;
|
||||||
|
}
|
||||||
|
set blueOpen(v: boolean) {
|
||||||
|
this.states.blueOpen = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
get blueFlash(): boolean {
|
||||||
|
return this.states.blueFlash;
|
||||||
|
}
|
||||||
|
set blueFlash(v: boolean) {
|
||||||
|
this.states.blueFlash = v;
|
||||||
|
}
|
||||||
|
get fleetMode(): boolean {
|
||||||
|
return this.states.fleetMode;
|
||||||
|
}
|
||||||
|
set fleetMode(v: boolean) {
|
||||||
|
this.states.fleetMode = v;
|
||||||
|
}
|
||||||
|
get ctrlFleetMode(): boolean {
|
||||||
|
return this.states.ctrlFleetMode;
|
||||||
|
}
|
||||||
|
set ctrlFleetMode(v: boolean) {
|
||||||
|
this.states.ctrlFleetMode = v;
|
||||||
|
}
|
||||||
|
get autoMode(): boolean {
|
||||||
|
return this.states.autoMode;
|
||||||
|
}
|
||||||
|
set autoMode(v: boolean) {
|
||||||
|
this.states.autoMode = v;
|
||||||
|
}
|
||||||
|
get ctrlAutoMode(): boolean {
|
||||||
|
return this.states.ctrlAutoMode;
|
||||||
|
}
|
||||||
|
set ctrlAutoMode(v: boolean) {
|
||||||
|
this.states.ctrlAutoMode = v;
|
||||||
|
}
|
||||||
|
get extinguish(): boolean {
|
||||||
|
return this.states.extinguish;
|
||||||
|
}
|
||||||
|
set extinguish(v: boolean) {
|
||||||
|
this.states.extinguish = v;
|
||||||
|
}
|
||||||
|
get approachLock(): boolean {
|
||||||
|
return this.states.approachLock;
|
||||||
|
}
|
||||||
|
set approachLock(v: boolean) {
|
||||||
|
this.states.approachLock = v;
|
||||||
|
}
|
||||||
|
get protectRoute(): boolean {
|
||||||
|
return this.states.protectRoute;
|
||||||
|
}
|
||||||
|
set protectRoute(v: boolean) {
|
||||||
|
this.states.protectRoute = v;
|
||||||
|
}
|
||||||
|
get autoRouteDisable(): boolean {
|
||||||
|
return this.states.autoRouteDisable;
|
||||||
|
}
|
||||||
|
set autoRouteDisable(v: boolean) {
|
||||||
|
this.states.autoRouteDisable = v;
|
||||||
|
}
|
||||||
|
get callon(): boolean {
|
||||||
|
return this.states.callon;
|
||||||
|
}
|
||||||
|
set callon(v: boolean) {
|
||||||
|
this.states.callon = v;
|
||||||
|
}
|
||||||
|
get yellowYellow(): boolean {
|
||||||
|
return this.states.yellowYellow;
|
||||||
|
}
|
||||||
|
set yellowYellow(v: boolean) {
|
||||||
|
this.states.yellowYellow = v;
|
||||||
|
}
|
||||||
|
get yellowGreen(): boolean {
|
||||||
|
return this.states.yellowGreen;
|
||||||
|
}
|
||||||
|
set yellowGreen(v: boolean) {
|
||||||
|
this.states.yellowGreen = v;
|
||||||
|
}
|
||||||
|
get blocked(): boolean {
|
||||||
|
return this.states.blocked;
|
||||||
|
}
|
||||||
|
set blocked(v: boolean) {
|
||||||
|
this.states.blocked = v;
|
||||||
|
}
|
||||||
|
get lampFailure(): boolean {
|
||||||
|
return this.states.lampFailure;
|
||||||
|
}
|
||||||
|
set lampFailure(v: boolean) {
|
||||||
|
this.states.lampFailure = v;
|
||||||
|
}
|
||||||
|
get states(): state.Signal {
|
||||||
|
return this.getState<state.Signal>();
|
||||||
|
}
|
||||||
|
clone(): SignalState {
|
||||||
|
return new SignalState(this.states.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: GraphicStateBase): void {
|
||||||
|
pb_1.Message.copyInto(data._state, this._state);
|
||||||
|
}
|
||||||
|
eq(data: GraphicStateBase): boolean {
|
||||||
|
return pb_1.Message.equals(this._state, data._state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mirrorFlipConfig: MenuItemOptions = {
|
||||||
|
name: '镜像翻转',
|
||||||
|
};
|
||||||
|
const signalCloseConfig: MenuItemOptions = {
|
||||||
|
name: '信号机关闭',
|
||||||
|
};
|
||||||
|
const signalRedFlashConfig: MenuItemOptions = {
|
||||||
|
name: '信号机红闪',
|
||||||
|
};
|
||||||
|
const signalOpenConfig: MenuItemOptions = {
|
||||||
|
name: '信号机开放',
|
||||||
|
};
|
||||||
|
const signalFleetConfig: MenuItemOptions = {
|
||||||
|
name: '连锁自动进路',
|
||||||
|
};
|
||||||
|
const humanControlConfig: MenuItemOptions = {
|
||||||
|
name: '进路交人工控',
|
||||||
|
};
|
||||||
|
const logicConfig: MenuItemOptions = {
|
||||||
|
name: '逻辑点灯',
|
||||||
|
};
|
||||||
|
const SignalEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '信号机编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [mirrorFlipConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const SignalOperateMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '信号机操作菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
signalCloseConfig,
|
||||||
|
signalOpenConfig,
|
||||||
|
signalFleetConfig,
|
||||||
|
humanControlConfig,
|
||||||
|
logicConfig,
|
||||||
|
signalRedFlashConfig,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
export class DrawSignalInteraction extends GraphicInteractionPlugin<Signal> {
|
||||||
|
static Name = 'signal_draw_right_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(DrawSignalInteraction.Name, app);
|
||||||
|
app.registerMenu(SignalEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new DrawSignalInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Signal.Type)
|
||||||
|
.map((g) => g as Signal);
|
||||||
|
}
|
||||||
|
bind(g: Signal): void {
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: Signal): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const signal = target.getGraphic() as Signal;
|
||||||
|
this.app.updateSelected(signal);
|
||||||
|
mirrorFlipConfig.handler = () => {
|
||||||
|
signal.mirror = !signal.mirror;
|
||||||
|
};
|
||||||
|
SignalEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SignalOperateInteraction extends GraphicInteractionPlugin<Signal> {
|
||||||
|
static Name = 'signal_operate_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(SignalOperateInteraction.Name, app);
|
||||||
|
app.registerMenu(SignalOperateMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new SignalOperateInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Signal.Type)
|
||||||
|
.map((g) => g as Signal);
|
||||||
|
}
|
||||||
|
bind(g: Signal): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.selectable = true;
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: Signal): void {
|
||||||
|
g.selectable = false;
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const signal = target.getGraphic() as Signal;
|
||||||
|
this.app.updateSelected(signal);
|
||||||
|
signalCloseConfig.handler = () => {
|
||||||
|
signal.states.redOpen = true;
|
||||||
|
signal.states.greenOpen = false;
|
||||||
|
signal.doRepaint();
|
||||||
|
};
|
||||||
|
signalOpenConfig.handler = () => {
|
||||||
|
signal.states.redOpen = false;
|
||||||
|
signal.states.greenOpen = true;
|
||||||
|
signal.doRepaint();
|
||||||
|
};
|
||||||
|
signalFleetConfig.handler = () => {
|
||||||
|
signal.states.fleetMode = true;
|
||||||
|
signal.doRepaint();
|
||||||
|
};
|
||||||
|
humanControlConfig.handler = () => {
|
||||||
|
signal.states.autoRouteDisable = true;
|
||||||
|
signal.doRepaint();
|
||||||
|
};
|
||||||
|
logicConfig.handler = () => {
|
||||||
|
signal.states.extinguish = true;
|
||||||
|
signal.doRepaint();
|
||||||
|
};
|
||||||
|
signalRedFlashConfig.handler = () => {
|
||||||
|
signal.states.redFlash = true;
|
||||||
|
signal.states.redOpen = false;
|
||||||
|
signal.states.greenOpen = false;
|
||||||
|
signal.doRepaint();
|
||||||
|
};
|
||||||
|
|
||||||
|
SignalOperateMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
178
src/drawApp/graphics/StationInteraction.ts
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
IStationData,
|
||||||
|
IStationState,
|
||||||
|
Station,
|
||||||
|
} from 'src/graphics/station/Station';
|
||||||
|
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 {
|
||||||
|
GraphicApp,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
|
||||||
|
import { KilometerSystem } from 'src/graphics/signal/Signal';
|
||||||
|
|
||||||
|
export class StationData extends GraphicDataBase implements IStationData {
|
||||||
|
constructor(data?: graphicData.Station) {
|
||||||
|
let station;
|
||||||
|
if (!data) {
|
||||||
|
station = new graphicData.Station({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Station.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
station = data;
|
||||||
|
}
|
||||||
|
super(station);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Station {
|
||||||
|
return this.getData<graphicData.Station>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get kilometerSystem(): KilometerSystem {
|
||||||
|
return this.data.kilometerSystem;
|
||||||
|
}
|
||||||
|
set kilometerSystem(v: KilometerSystem) {
|
||||||
|
this.data.kilometerSystem = new graphicData.KilometerSystem(v);
|
||||||
|
}
|
||||||
|
get hasControl(): boolean {
|
||||||
|
return this.data.hasControl;
|
||||||
|
}
|
||||||
|
set hasControl(v: boolean) {
|
||||||
|
this.data.hasControl = v;
|
||||||
|
}
|
||||||
|
get concentrationStations(): boolean {
|
||||||
|
return this.data.concentrationStations;
|
||||||
|
}
|
||||||
|
set concentrationStations(v: boolean) {
|
||||||
|
this.data.concentrationStations = v;
|
||||||
|
}
|
||||||
|
clone(): StationData {
|
||||||
|
return new StationData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: StationData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: StationData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StationState extends GraphicStateBase implements IStationState {
|
||||||
|
constructor(proto?: state.Rtu) {
|
||||||
|
let states;
|
||||||
|
if (proto) {
|
||||||
|
states = proto;
|
||||||
|
} else {
|
||||||
|
states = new state.Station();
|
||||||
|
}
|
||||||
|
super(states, Station.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
get ipRtuStusDown(): boolean {
|
||||||
|
return this.states.ipRtuStusDown;
|
||||||
|
}
|
||||||
|
set ipRtuStusDown(v: boolean) {
|
||||||
|
this.states.ipRtuStusDown = v;
|
||||||
|
}
|
||||||
|
get ipRtuStusInLocalCtrl(): boolean {
|
||||||
|
return this.states.ipRtuStusInLocalCtrl;
|
||||||
|
}
|
||||||
|
set ipRtuStusInLocalCtrl(v: boolean) {
|
||||||
|
this.states.ipRtuStusInLocalCtrl = v;
|
||||||
|
}
|
||||||
|
get ipRtuStusInCentralCtrl(): boolean {
|
||||||
|
return this.states.ipRtuStusInCentralCtrl;
|
||||||
|
}
|
||||||
|
set ipRtuStusInCentralCtrl(v: boolean) {
|
||||||
|
this.states.ipRtuStusInCentralCtrl = v;
|
||||||
|
}
|
||||||
|
get ipRtuStusInEmergencyCtrl(): boolean {
|
||||||
|
return this.states.ipRtuStusInEmergencyCtrl;
|
||||||
|
}
|
||||||
|
set ipRtuStusInEmergencyCtrl(v: boolean) {
|
||||||
|
this.states.ipRtuStusInEmergencyCtrl = v;
|
||||||
|
}
|
||||||
|
get states(): state.Rtu {
|
||||||
|
return this.getState<state.Rtu>();
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): StationState {
|
||||||
|
return new StationState(this.states.cloneMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const powerUnlockConfig: MenuItemOptions = {
|
||||||
|
name: '上电解锁',
|
||||||
|
};
|
||||||
|
|
||||||
|
const chainConfig: MenuItemOptions = {
|
||||||
|
name: '全站设置连锁自动触发',
|
||||||
|
};
|
||||||
|
const removeChainConfig: MenuItemOptions = {
|
||||||
|
name: '全站取消连锁自动触发',
|
||||||
|
};
|
||||||
|
const StationOperateMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '车站操作菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [powerUnlockConfig, chainConfig, removeChainConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export class StationOperateInteraction extends GraphicInteractionPlugin<Station> {
|
||||||
|
static Name = 'station_operate_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(StationOperateInteraction.Name, app);
|
||||||
|
app.registerMenu(StationOperateMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new StationOperateInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Station[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Station.Type)
|
||||||
|
.map((g) => g as Station);
|
||||||
|
}
|
||||||
|
bind(g: Station): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.selectable = true;
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: Station): void {
|
||||||
|
g.selectable = false;
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const station = target.getGraphic() as Station;
|
||||||
|
this.app.updateSelected(station);
|
||||||
|
powerUnlockConfig.handler = () => {
|
||||||
|
station.states.ipRtuStusInLocalCtrl = true;
|
||||||
|
station.doRepaint();
|
||||||
|
};
|
||||||
|
chainConfig.handler = () => {
|
||||||
|
station.states.ipRtuStusDown = true;
|
||||||
|
station.doRepaint();
|
||||||
|
};
|
||||||
|
removeChainConfig.handler = () => {
|
||||||
|
console.log(2222);
|
||||||
|
};
|
||||||
|
StationOperateMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
55
src/drawApp/graphics/StationLineInteraction.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
IStationLineData,
|
||||||
|
StationLine,
|
||||||
|
} from 'src/graphics/stationLine/StationLine';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class StationLineData
|
||||||
|
extends GraphicDataBase
|
||||||
|
implements IStationLineData
|
||||||
|
{
|
||||||
|
constructor(data?: graphicData.StationLine) {
|
||||||
|
let stationLine;
|
||||||
|
if (!data) {
|
||||||
|
stationLine = new graphicData.StationLine({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(StationLine.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
stationLine = data;
|
||||||
|
}
|
||||||
|
super(stationLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.StationLine {
|
||||||
|
return this.getData<graphicData.StationLine>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get hasTransfer(): boolean {
|
||||||
|
return this.data.hasTransfer;
|
||||||
|
}
|
||||||
|
set hasTransfer(v: boolean) {
|
||||||
|
this.data.hasTransfer = v;
|
||||||
|
}
|
||||||
|
get hideName(): boolean {
|
||||||
|
return this.data.hideName;
|
||||||
|
}
|
||||||
|
set hideName(v: boolean) {
|
||||||
|
this.data.hideName = v;
|
||||||
|
}
|
||||||
|
clone(): StationLineData {
|
||||||
|
return new StationLineData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: StationLineData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: StationLineData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
296
src/drawApp/graphics/TrainInteraction.ts
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { ITrainData, ITrainState, Train } from 'src/graphics/train/Train';
|
||||||
|
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 {
|
||||||
|
GraphicApp,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
|
||||||
|
|
||||||
|
export class TrainData extends GraphicDataBase implements ITrainData {
|
||||||
|
constructor(data?: graphicData.Train) {
|
||||||
|
let train;
|
||||||
|
if (!data) {
|
||||||
|
train = new graphicData.Train({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Train.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
train = data;
|
||||||
|
}
|
||||||
|
super(train);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Train {
|
||||||
|
return this.getData<graphicData.Train>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
clone(): TrainData {
|
||||||
|
return new TrainData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: TrainData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: TrainData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrainState extends GraphicStateBase implements ITrainState {
|
||||||
|
constructor(proto?: StateTrain) {
|
||||||
|
let states;
|
||||||
|
if (proto) {
|
||||||
|
states = proto;
|
||||||
|
} else {
|
||||||
|
states = new StateTrain();
|
||||||
|
}
|
||||||
|
super(states, Train.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
get states(): StateTrain {
|
||||||
|
return this.getState<StateTrain>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get lineId(): number {
|
||||||
|
return this.states.lineId;
|
||||||
|
}
|
||||||
|
set lineId(v: number) {
|
||||||
|
this.states.lineId = v;
|
||||||
|
}
|
||||||
|
get rtuId(): number {
|
||||||
|
return this.states.rtuId;
|
||||||
|
}
|
||||||
|
set rtuId(v: number) {
|
||||||
|
this.states.rtuId = v;
|
||||||
|
}
|
||||||
|
get window(): train.NccWindow {
|
||||||
|
if (!this.states.window) {
|
||||||
|
this.states.window = new train.NccWindow();
|
||||||
|
}
|
||||||
|
return this.states.window;
|
||||||
|
}
|
||||||
|
set window(v: train.NccWindow) {
|
||||||
|
this.states.window = v;
|
||||||
|
}
|
||||||
|
get devType(): number {
|
||||||
|
return this.states.devType;
|
||||||
|
}
|
||||||
|
set devType(v: number) {
|
||||||
|
this.states.devType = v;
|
||||||
|
}
|
||||||
|
get devName(): string {
|
||||||
|
return this.states.devName;
|
||||||
|
}
|
||||||
|
set devName(v: string) {
|
||||||
|
this.states.devName = v;
|
||||||
|
}
|
||||||
|
get id(): string {
|
||||||
|
return this.states.id;
|
||||||
|
}
|
||||||
|
set id(v: string) {
|
||||||
|
this.states.id = v;
|
||||||
|
}
|
||||||
|
get groupId(): string {
|
||||||
|
return this.states.groupId;
|
||||||
|
}
|
||||||
|
set groupId(v: string) {
|
||||||
|
this.states.groupId = v;
|
||||||
|
}
|
||||||
|
get trainId(): string {
|
||||||
|
return this.states.trainId;
|
||||||
|
}
|
||||||
|
set trainId(v: string) {
|
||||||
|
this.states.trainId = v;
|
||||||
|
}
|
||||||
|
get globalId(): string {
|
||||||
|
return this.states.globalId;
|
||||||
|
}
|
||||||
|
set globalId(v: string) {
|
||||||
|
this.states.globalId = v;
|
||||||
|
}
|
||||||
|
get destinationId(): number {
|
||||||
|
return this.states.destinationId;
|
||||||
|
}
|
||||||
|
set destinationId(v: number) {
|
||||||
|
this.states.destinationId = v;
|
||||||
|
}
|
||||||
|
get rollingStock(): number {
|
||||||
|
return this.states.rollingStock;
|
||||||
|
}
|
||||||
|
set rollingStock(v: number) {
|
||||||
|
this.states.rollingStock = v;
|
||||||
|
}
|
||||||
|
get driverId(): string {
|
||||||
|
return this.states.driverId;
|
||||||
|
}
|
||||||
|
set driverId(v: string) {
|
||||||
|
this.states.driverId = v;
|
||||||
|
}
|
||||||
|
get otpTime(): number {
|
||||||
|
return this.states.otpTime;
|
||||||
|
}
|
||||||
|
set otpTime(v: number) {
|
||||||
|
this.states.otpTime = v;
|
||||||
|
}
|
||||||
|
get mode(): state.TrainMode {
|
||||||
|
if (!this.states.mode) {
|
||||||
|
this.states.mode = new state.TrainMode();
|
||||||
|
}
|
||||||
|
return this.states.mode;
|
||||||
|
}
|
||||||
|
set mode(v: state.TrainMode) {
|
||||||
|
this.states.mode = v;
|
||||||
|
}
|
||||||
|
get arriveTime(): number {
|
||||||
|
return this.states.arriveTime;
|
||||||
|
}
|
||||||
|
set arriveTime(v: number) {
|
||||||
|
this.states.arriveTime = v;
|
||||||
|
}
|
||||||
|
get departTime(): number {
|
||||||
|
return this.states.departTime;
|
||||||
|
}
|
||||||
|
set departTime(v: number) {
|
||||||
|
this.states.departTime = v;
|
||||||
|
}
|
||||||
|
get speed(): number {
|
||||||
|
return this.states.speed;
|
||||||
|
}
|
||||||
|
set speed(v: number) {
|
||||||
|
this.states.speed = v;
|
||||||
|
}
|
||||||
|
get show(): boolean {
|
||||||
|
return this.states.show;
|
||||||
|
}
|
||||||
|
set show(v: boolean) {
|
||||||
|
this.states.show = v;
|
||||||
|
}
|
||||||
|
get type(): boolean {
|
||||||
|
return this.states.type;
|
||||||
|
}
|
||||||
|
set type(v: boolean) {
|
||||||
|
this.states.type = v;
|
||||||
|
}
|
||||||
|
get routeId(): number {
|
||||||
|
return this.states.routeId;
|
||||||
|
}
|
||||||
|
set routeId(v: number) {
|
||||||
|
this.states.routeId = v;
|
||||||
|
}
|
||||||
|
get rate(): number {
|
||||||
|
return this.states.rate;
|
||||||
|
}
|
||||||
|
set rate(v: number) {
|
||||||
|
this.states.rate = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): TrainState {
|
||||||
|
return new TrainState(this.states.cloneMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StateTrain extends train.TrainInfo {
|
||||||
|
id: string;
|
||||||
|
constructor(data?: train.TrainInfo) {
|
||||||
|
super(data);
|
||||||
|
if (data?.trainIndex) {
|
||||||
|
this.id = data?.trainIndex;
|
||||||
|
} else {
|
||||||
|
this.id = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const negativeDirectionConfig: MenuItemOptions = {
|
||||||
|
name: '反方向运行',
|
||||||
|
};
|
||||||
|
const HoldTrainConfig: MenuItemOptions = {
|
||||||
|
name: '扣车',
|
||||||
|
};
|
||||||
|
const openDoorConfig: MenuItemOptions = {
|
||||||
|
name: '开门',
|
||||||
|
};
|
||||||
|
const editGroupConfig: MenuItemOptions = {
|
||||||
|
name: '修改车组号',
|
||||||
|
};
|
||||||
|
const TrainOperateMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '列车操作菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
negativeDirectionConfig,
|
||||||
|
HoldTrainConfig,
|
||||||
|
openDoorConfig,
|
||||||
|
editGroupConfig,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export class TrainOperateInteraction extends GraphicInteractionPlugin<Train> {
|
||||||
|
static Name = 'train_operate_menu';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(TrainOperateInteraction.Name, app);
|
||||||
|
app.registerMenu(TrainOperateMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp) {
|
||||||
|
return new TrainOperateInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Train[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type === Train.Type).map((g) => g as Train);
|
||||||
|
}
|
||||||
|
bind(g: Train): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.selectable = true;
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(g: Train): void {
|
||||||
|
g.selectable = false;
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const train = target.getGraphic() as Train;
|
||||||
|
this.app.updateSelected(train);
|
||||||
|
negativeDirectionConfig.handler = () => {
|
||||||
|
const mode = train.states.mode;
|
||||||
|
if (!train.states.mode.ipModeTrainDirUp) {
|
||||||
|
mode.ipModeTrainDirUp = true;
|
||||||
|
mode.ipModeTrainDirDown = false;
|
||||||
|
} else if (!train.states.mode.ipModeTrainDirDown) {
|
||||||
|
mode.ipModeTrainDirUp = false;
|
||||||
|
mode.ipModeTrainDirDown = true;
|
||||||
|
}
|
||||||
|
train.chagneDirection();
|
||||||
|
};
|
||||||
|
HoldTrainConfig.handler = () => {
|
||||||
|
train.states.mode.ipModeTrainHolded =
|
||||||
|
!train.states.mode.ipModeTrainHolded;
|
||||||
|
train.chagneState();
|
||||||
|
};
|
||||||
|
openDoorConfig.handler = () => {
|
||||||
|
train.states.mode.ipModeTrainDoorOpen =
|
||||||
|
!train.states.mode.ipModeTrainDoorOpen;
|
||||||
|
train.chagneState();
|
||||||
|
};
|
||||||
|
editGroupConfig.handler = () => {
|
||||||
|
train.states.trainId = '022';
|
||||||
|
train.doRepaint();
|
||||||
|
};
|
||||||
|
TrainOperateMenu.open(e.global);
|
||||||
|
}
|
||||||
|
}
|
110
src/drawApp/graphics/TrainLineInteraction.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
ITrainLineData,
|
||||||
|
TrainLine,
|
||||||
|
ITrainLineState,
|
||||||
|
} from 'src/graphics/trainLine/TrainLine';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
|
||||||
|
import { state } from 'src/protos/ws_message';
|
||||||
|
|
||||||
|
export class TrainLineData extends GraphicDataBase implements ITrainLineData {
|
||||||
|
constructor(data?: graphicData.TrainLine) {
|
||||||
|
let fan;
|
||||||
|
if (data) {
|
||||||
|
fan = data;
|
||||||
|
} else {
|
||||||
|
fan = new graphicData.TrainLine({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(TrainLine.Type),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super(fan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.TrainLine {
|
||||||
|
return this.getData<graphicData.TrainLine>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
clone(): TrainLineData {
|
||||||
|
return new TrainLineData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: TrainLineData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: TrainLineData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrainLineState
|
||||||
|
extends GraphicStateBase
|
||||||
|
implements ITrainLineState
|
||||||
|
{
|
||||||
|
constructor(proto?: state.WsLineNetTrainOffsetMessage) {
|
||||||
|
let states;
|
||||||
|
if (proto) {
|
||||||
|
states = proto;
|
||||||
|
} else {
|
||||||
|
states = new state.WsLineNetTrainOffsetMessage();
|
||||||
|
}
|
||||||
|
super(states, TrainLine.Type);
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.states.groupId;
|
||||||
|
}
|
||||||
|
get lineId(): number {
|
||||||
|
return this.states.lineId;
|
||||||
|
}
|
||||||
|
set lineId(v: number) {
|
||||||
|
this.states.lineId = v;
|
||||||
|
}
|
||||||
|
get trainIndex(): string {
|
||||||
|
return this.states.trainIndex;
|
||||||
|
}
|
||||||
|
set trainIndex(v: string) {
|
||||||
|
this.states.trainIndex = v;
|
||||||
|
}
|
||||||
|
get groupId(): string {
|
||||||
|
return this.states.groupId;
|
||||||
|
}
|
||||||
|
set groupId(v: string) {
|
||||||
|
this.states.groupId = v;
|
||||||
|
}
|
||||||
|
get show(): boolean {
|
||||||
|
return this.states.show;
|
||||||
|
}
|
||||||
|
set show(v: boolean) {
|
||||||
|
this.states.show = v;
|
||||||
|
}
|
||||||
|
get kilometerCode(): number {
|
||||||
|
return this.states.kilometerCode;
|
||||||
|
}
|
||||||
|
set kilometerCode(v: number) {
|
||||||
|
this.states.kilometerCode = v;
|
||||||
|
}
|
||||||
|
get dir(): number {
|
||||||
|
return this.states.dir;
|
||||||
|
}
|
||||||
|
set dir(v: number) {
|
||||||
|
this.states.dir = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
get states(): state.WsLineNetTrainOffsetMessage {
|
||||||
|
return this.getState<state.WsLineNetTrainOffsetMessage>();
|
||||||
|
}
|
||||||
|
clone(): TrainLineState {
|
||||||
|
return new TrainLineState(this.states.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: GraphicStateBase): void {
|
||||||
|
pb_1.Message.copyInto(data._state, this._state);
|
||||||
|
}
|
||||||
|
eq(data: GraphicStateBase): boolean {
|
||||||
|
return pb_1.Message.equals(this._state, data._state);
|
||||||
|
}
|
||||||
|
}
|
49
src/drawApp/graphics/TrainWindowInteraction.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import {
|
||||||
|
ITrainWindowData,
|
||||||
|
TrainWindow,
|
||||||
|
} from 'src/graphics/trainWindow/TrainWindow';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class TrainWindowData
|
||||||
|
extends GraphicDataBase
|
||||||
|
implements ITrainWindowData
|
||||||
|
{
|
||||||
|
constructor(data?: graphicData.TrainWindow) {
|
||||||
|
let trainWindow;
|
||||||
|
if (!data) {
|
||||||
|
trainWindow = new graphicData.TrainWindow({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(TrainWindow.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
trainWindow = data;
|
||||||
|
}
|
||||||
|
super(trainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.TrainWindow {
|
||||||
|
return this.getData<graphicData.TrainWindow>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get sectionId(): string {
|
||||||
|
return this.data.sectionId;
|
||||||
|
}
|
||||||
|
set sectionId(v: string) {
|
||||||
|
this.data.sectionId = v;
|
||||||
|
}
|
||||||
|
clone(): TrainWindowData {
|
||||||
|
return new TrainWindowData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: TrainWindowData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: TrainWindowData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
93
src/drawApp/graphics/TurnoutInteraction.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { ITurnoutData, Turnout } from 'src/graphics/turnout/Turnout';
|
||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { KilometerSystem } from 'src/graphics/signal/Signal';
|
||||||
|
|
||||||
|
function getDefaultEndPoint() {
|
||||||
|
return {
|
||||||
|
pointA: [new graphicData.Point([50, 0])],
|
||||||
|
pointB: [new graphicData.Point([-50, 0])],
|
||||||
|
pointC: [new graphicData.Point([-50, -50])],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TurnoutData extends GraphicDataBase implements ITurnoutData {
|
||||||
|
constructor(data?: graphicData.Turnout) {
|
||||||
|
let turnout = new graphicData.Turnout();
|
||||||
|
if (!data) {
|
||||||
|
turnout.common = GraphicDataBase.defaultCommonInfo(Turnout.Type);
|
||||||
|
const p = getDefaultEndPoint();
|
||||||
|
turnout.pointA = p.pointA;
|
||||||
|
turnout.pointB = p.pointB;
|
||||||
|
turnout.pointC = p.pointC;
|
||||||
|
} else {
|
||||||
|
turnout = data;
|
||||||
|
}
|
||||||
|
super(turnout);
|
||||||
|
}
|
||||||
|
get data(): graphicData.Turnout {
|
||||||
|
return this.getData<graphicData.Turnout>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get pointA(): IPointData[] {
|
||||||
|
return this.data.pointA;
|
||||||
|
}
|
||||||
|
set pointA(v: IPointData[]) {
|
||||||
|
this.data.pointA = v.map((p) => new graphicData.Point({ x: p.x, y: p.y }));
|
||||||
|
}
|
||||||
|
get pointB(): IPointData[] {
|
||||||
|
return this.data.pointB;
|
||||||
|
}
|
||||||
|
set pointB(v: IPointData[]) {
|
||||||
|
this.data.pointB = v.map((p) => new graphicData.Point({ x: p.x, y: p.y }));
|
||||||
|
}
|
||||||
|
get pointC(): IPointData[] {
|
||||||
|
return this.data.pointC;
|
||||||
|
}
|
||||||
|
set pointC(v: IPointData[]) {
|
||||||
|
this.data.pointC = v.map((p) => new graphicData.Point({ x: p.x, y: p.y }));
|
||||||
|
}
|
||||||
|
get paRef(): graphicData.RelatedRef {
|
||||||
|
return this.data.paRef;
|
||||||
|
}
|
||||||
|
set paRef(ref: graphicData.RelatedRef) {
|
||||||
|
this.data.paRef = ref;
|
||||||
|
}
|
||||||
|
get pbRef(): graphicData.RelatedRef {
|
||||||
|
return this.data.pbRef;
|
||||||
|
}
|
||||||
|
set pbRef(ref: graphicData.RelatedRef) {
|
||||||
|
this.data.pbRef = ref;
|
||||||
|
}
|
||||||
|
get pcRef(): graphicData.RelatedRef {
|
||||||
|
return this.data.pcRef;
|
||||||
|
}
|
||||||
|
set pcRef(ref: graphicData.RelatedRef) {
|
||||||
|
this.data.pcRef = ref;
|
||||||
|
}
|
||||||
|
get kilometerSystem(): KilometerSystem[] {
|
||||||
|
return this.data.kilometerSystem;
|
||||||
|
}
|
||||||
|
set kilometerSystem(value: KilometerSystem[]) {
|
||||||
|
this.data.kilometerSystem = value.map(
|
||||||
|
(v) => new graphicData.KilometerSystem(v)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
clone(): TurnoutData {
|
||||||
|
return new TurnoutData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: TurnoutData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: TurnoutData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
393
src/drawApp/index.ts
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
import { fromUint8Array, toUint8Array } from 'js-base64';
|
||||||
|
import { IPointData, Point } from 'pixi.js';
|
||||||
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { Link } from 'src/graphics/link/Link';
|
||||||
|
import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant';
|
||||||
|
import { Train, TrainTemplate } from 'src/graphics/train/Train';
|
||||||
|
import { TrainDraw } from 'src/graphics/train/TrainDrawAssistant';
|
||||||
|
import { Signal, SignalTemplate } from 'src/graphics/signal/Signal';
|
||||||
|
import { SignalDraw } from 'src/graphics/signal/SignalDrawAssistant';
|
||||||
|
import {
|
||||||
|
CombinationKey,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicData,
|
||||||
|
GraphicTransform,
|
||||||
|
JlDrawApp,
|
||||||
|
KeyListener,
|
||||||
|
} 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';
|
||||||
|
import {
|
||||||
|
SignalData,
|
||||||
|
DrawSignalInteraction,
|
||||||
|
SignalState,
|
||||||
|
} from './graphics/SignalInteraction';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { Rect, RectTemplate } from 'src/graphics/rect/Rect';
|
||||||
|
import { RectDraw } from 'src/graphics/rect/RectDrawAssistant';
|
||||||
|
import { RectData } from './graphics/RectInteraction';
|
||||||
|
import { Platform, PlatformTemplate } from 'src/graphics/platform/Platform';
|
||||||
|
import { PlatformData, PlatformState } from './graphics/PlatformInteraction';
|
||||||
|
import { PlatformDraw } from 'src/graphics/platform/PlatformDrawAssistant';
|
||||||
|
import { Station, StationTemplate } from 'src/graphics/station/Station';
|
||||||
|
import { StationDraw } from 'src/graphics/station/StationDrawAssistant';
|
||||||
|
import { StationData, StationState } from './graphics/StationInteraction';
|
||||||
|
import {
|
||||||
|
StationLine,
|
||||||
|
StationLineTemplate,
|
||||||
|
} from 'src/graphics/stationLine/StationLine';
|
||||||
|
import { StationLineDraw } from 'src/graphics/stationLine/StationLineDrawAssistant';
|
||||||
|
import { StationLineData } from './graphics/StationLineInteraction';
|
||||||
|
import {
|
||||||
|
ItrainLineTemplate,
|
||||||
|
TrainLine,
|
||||||
|
} from 'src/graphics/trainLine/TrainLine';
|
||||||
|
import { TrainLineDraw } from 'src/graphics/trainLine/TrainLineAssistant';
|
||||||
|
import { TrainLineData } from './graphics/TrainLineInteraction';
|
||||||
|
import {
|
||||||
|
OneClickGenerateDraw,
|
||||||
|
OneClickGenerateTemplate,
|
||||||
|
} from 'src/graphics/trainWindow/oneClickDrawAssistant';
|
||||||
|
import {
|
||||||
|
TrainWindow,
|
||||||
|
TrainWindowTemplate,
|
||||||
|
} from 'src/graphics/trainWindow/TrainWindow';
|
||||||
|
import { TrainWindowDraw } from 'src/graphics/trainWindow/TrainWindowDrawAssistant';
|
||||||
|
import { TrainWindowData } from './graphics/TrainWindowInteraction';
|
||||||
|
import {
|
||||||
|
AxleCounting,
|
||||||
|
AxleCountingTemplate,
|
||||||
|
} from 'src/graphics/axleCounting/AxleCounting';
|
||||||
|
import { AxleCountingDraw } from 'src/graphics/axleCounting/AxleCountingDrawAssistant';
|
||||||
|
import { AxleCountingData } from './graphics/AxleCountingInteraction';
|
||||||
|
import { Turnout, TurnoutTemplate } from 'src/graphics/turnout/Turnout';
|
||||||
|
import { TurnoutDraw } from 'src/graphics/turnout/TurnoutDrawAssistant';
|
||||||
|
import { TurnoutData } from './graphics/TurnoutInteraction';
|
||||||
|
import { RunLine, RunLineTemplate } from 'src/graphics/runLine/RunLine';
|
||||||
|
import { RunLineDraw } from 'src/graphics/runLine/RunLineDrawAssistant';
|
||||||
|
import { RunLineData, DrawRunLinePlugin } from './graphics/RunLineInteraction';
|
||||||
|
import { saveDraft, getDraft } from 'src/api/DraftApi';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { successNotify, errorNotify } from '../utils/CommonNotify';
|
||||||
|
import { Section, SectionTemplate } from 'src/graphics/section/Section';
|
||||||
|
import { SectionDraw } from 'src/graphics/section/SectionDrawAssistant';
|
||||||
|
import { SectionData } from './graphics/SectionInteraction';
|
||||||
|
import { PathLine, PathLineTemplate } from 'src/graphics/pathLine/PathLine';
|
||||||
|
import { PathLineDraw } from 'src/graphics/pathLine/PathLineDrawAssistant';
|
||||||
|
import { PathLineData } from './graphics/PathLineInteraction';
|
||||||
|
import { toStorageTransform } from './graphics/GraphicDataBase';
|
||||||
|
import { SeparatorDraw } from 'src/graphics/separator/SeparatorDrawAssistant';
|
||||||
|
import { Separator, SeparatorTemplate } from 'src/graphics/separator/Separator';
|
||||||
|
import { SeparatorData } from './graphics/SeparatorInteraction';
|
||||||
|
|
||||||
|
// export function fromStoragePoint(p: graphicData.Point): Point {
|
||||||
|
// return new Point(p.x, p.y);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function toStoragePoint(p: IPointData): graphicData.Point {
|
||||||
|
// return new graphicData.Point({ x: p.x, y: p.y });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function fromStorageTransfrom(
|
||||||
|
// transfrom: graphicData.Transform
|
||||||
|
// ): GraphicTransform {
|
||||||
|
// return new GraphicTransform(
|
||||||
|
// fromStoragePoint(transfrom.position),
|
||||||
|
// fromStoragePoint(transfrom.scale),
|
||||||
|
// transfrom.rotation,
|
||||||
|
// fromStoragePoint(transfrom.skew)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function toStorageTransform(
|
||||||
|
// transform: GraphicTransform
|
||||||
|
// ): graphicData.Transform {
|
||||||
|
// return new graphicData.Transform({
|
||||||
|
// position: toStoragePoint(transform.position),
|
||||||
|
// scale: toStoragePoint(transform.scale),
|
||||||
|
// rotation: transform.rotation,
|
||||||
|
// skew: toStoragePoint(transform.skew),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
const UndoOptions: MenuItemOptions = {
|
||||||
|
name: '撤销',
|
||||||
|
};
|
||||||
|
const RedoOptions: MenuItemOptions = {
|
||||||
|
name: '重做',
|
||||||
|
};
|
||||||
|
const SelectAllOptions: MenuItemOptions = {
|
||||||
|
name: '全选',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultCanvasMenu = new ContextMenu({
|
||||||
|
name: '绘制-画布菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [UndoOptions, RedoOptions],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [SelectAllOptions],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let drawApp: JlDrawApp | null = null;
|
||||||
|
|
||||||
|
export function getDrawApp(): JlDrawApp | null {
|
||||||
|
return drawApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function destroyDrawApp(): void {
|
||||||
|
if (drawApp) {
|
||||||
|
drawApp.destroy();
|
||||||
|
drawApp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initDrawApp(dom: HTMLElement): JlDrawApp {
|
||||||
|
drawApp = new JlDrawApp(dom);
|
||||||
|
const app = drawApp;
|
||||||
|
//根据草稿图类型加载绘图工具
|
||||||
|
const draftType = useDrawStore().$state.draftType;
|
||||||
|
let drawAssistants: (
|
||||||
|
| PlatformDraw
|
||||||
|
| StationDraw
|
||||||
|
| SignalDraw
|
||||||
|
| TurnoutDraw
|
||||||
|
| RunLineDraw
|
||||||
|
| SectionDraw
|
||||||
|
| StationLineDraw
|
||||||
|
| RectDraw
|
||||||
|
| TrainLineDraw
|
||||||
|
| PathLineDraw
|
||||||
|
| TrainWindowDraw
|
||||||
|
| TrainDraw
|
||||||
|
| OneClickGenerateDraw
|
||||||
|
| AxleCountingDraw
|
||||||
|
| SeparatorDraw
|
||||||
|
)[] = [];
|
||||||
|
if (draftType === 'Line') {
|
||||||
|
drawAssistants = [
|
||||||
|
new PlatformDraw(
|
||||||
|
app,
|
||||||
|
new PlatformTemplate(new PlatformData(), new PlatformState())
|
||||||
|
),
|
||||||
|
new StationDraw(
|
||||||
|
app,
|
||||||
|
new StationTemplate(new StationData(), new StationState())
|
||||||
|
),
|
||||||
|
new SignalDraw(
|
||||||
|
app,
|
||||||
|
new SignalTemplate(new SignalData(), new SignalState())
|
||||||
|
),
|
||||||
|
new TrainDraw(app, new TrainTemplate(new TrainData(), new TrainState())),
|
||||||
|
new SectionDraw(app, new SectionTemplate(new SectionData())),
|
||||||
|
new TurnoutDraw(app, new TurnoutTemplate(new TurnoutData())),
|
||||||
|
new TrainWindowDraw(app, new TrainWindowTemplate(new TrainWindowData())),
|
||||||
|
new OneClickGenerateDraw(app, new OneClickGenerateTemplate()),
|
||||||
|
new AxleCountingDraw(
|
||||||
|
app,
|
||||||
|
new AxleCountingTemplate(new AxleCountingData())
|
||||||
|
),
|
||||||
|
new SeparatorDraw(app, new SeparatorTemplate(new SeparatorData())),
|
||||||
|
];
|
||||||
|
DrawSignalInteraction.init(app);
|
||||||
|
} else {
|
||||||
|
drawAssistants = [
|
||||||
|
new StationLineDraw(app, new StationLineTemplate(new StationLineData())),
|
||||||
|
new RectDraw(app, new RectTemplate(new RectData())),
|
||||||
|
new RunLineDraw(app, new RunLineTemplate(new RunLineData())),
|
||||||
|
new TrainLineDraw(app, new ItrainLineTemplate(new TrainLineData())),
|
||||||
|
new PathLineDraw(app, new PathLineTemplate(new PathLineData())),
|
||||||
|
];
|
||||||
|
DrawRunLinePlugin.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.setOptions({ drawAssistants: drawAssistants });
|
||||||
|
|
||||||
|
// 画布右键菜单
|
||||||
|
app.registerMenu(DefaultCanvasMenu);
|
||||||
|
app.canvas.on('_rightclick', (e) => {
|
||||||
|
if (app._drawing) return;
|
||||||
|
UndoOptions.disabled = !app.opRecord.hasUndo;
|
||||||
|
RedoOptions.disabled = !app.opRecord.hasRedo;
|
||||||
|
UndoOptions.handler = () => {
|
||||||
|
app.opRecord.undo();
|
||||||
|
};
|
||||||
|
RedoOptions.handler = () => {
|
||||||
|
app.opRecord.redo();
|
||||||
|
};
|
||||||
|
SelectAllOptions.handler = () => {
|
||||||
|
app.selectAllGraphics();
|
||||||
|
};
|
||||||
|
DefaultCanvasMenu.open(e.global);
|
||||||
|
});
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyS',
|
||||||
|
global: true,
|
||||||
|
combinations: [CombinationKey.Ctrl],
|
||||||
|
onPress: () => {
|
||||||
|
saveDrawToServer(app);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return drawApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveDrawToServer(app: JlDrawApp) {
|
||||||
|
const base64 = saveDrawDatas(app);
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const id = drawStore.draftId;
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saveDraft(id as number, { proto: base64 })
|
||||||
|
.then(() => {
|
||||||
|
successNotify('保存数据成功!');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
errorNotify(err.message, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// const StorageKey = 'graphic-storage';
|
||||||
|
export function saveDrawDatas(app: JlDrawApp) {
|
||||||
|
const storage = new graphicData.RtssGraphicStorage();
|
||||||
|
const canvasData = app.canvas.saveData();
|
||||||
|
storage.canvas = new graphicData.Canvas({
|
||||||
|
...canvasData,
|
||||||
|
viewportTransform: toStorageTransform(canvasData.viewportTransform),
|
||||||
|
});
|
||||||
|
const graphics = app.queryStore.getAllGraphics();
|
||||||
|
graphics.forEach((g) => {
|
||||||
|
if (Link.Type === g.type) {
|
||||||
|
const linkData = (g as Link).saveData();
|
||||||
|
storage.links.push((linkData as LinkData).data);
|
||||||
|
} else if (Rect.Type === g.type) {
|
||||||
|
const rectData = (g as Rect).saveData();
|
||||||
|
storage.rects.push((rectData as RectData).data);
|
||||||
|
} else if (IscsFan.Type === g.type) {
|
||||||
|
const IscsFanData = (g as IscsFan).saveData();
|
||||||
|
storage.iscsFans.push((IscsFanData as IscsFanData).data);
|
||||||
|
} else if (Platform.Type === g.type) {
|
||||||
|
const platformData = (g as Platform).saveData();
|
||||||
|
storage.Platforms.push((platformData as PlatformData).data);
|
||||||
|
} else if (Station.Type === g.type) {
|
||||||
|
const stationData = (g as Station).saveData();
|
||||||
|
storage.stations.push((stationData as StationData).data);
|
||||||
|
} else if (Train.Type === g.type) {
|
||||||
|
const trainData = (g as Train).saveData();
|
||||||
|
storage.train.push((trainData as TrainData).data);
|
||||||
|
} else if (Turnout.Type === g.type) {
|
||||||
|
const turnoutData = (g as Turnout).saveData();
|
||||||
|
storage.turnouts.push((turnoutData as TurnoutData).data);
|
||||||
|
} else if (Signal.Type === g.type) {
|
||||||
|
const signalData = (g as Signal).saveData();
|
||||||
|
storage.signals.push((signalData as SignalData).data);
|
||||||
|
} else if (RunLine.Type === g.type) {
|
||||||
|
const runLineData = (g as RunLine).saveData();
|
||||||
|
storage.runLines.push((runLineData as RunLineData).data);
|
||||||
|
} else if (Section.Type === g.type) {
|
||||||
|
const sectionData = (g as Section).saveData();
|
||||||
|
storage.section.push((sectionData as SectionData).data);
|
||||||
|
} else if (StationLine.Type === g.type) {
|
||||||
|
const stationLineData = (g as StationLine).saveData();
|
||||||
|
storage.stationLines.push((stationLineData as StationLineData).data);
|
||||||
|
} else if (TrainLine.Type === g.type) {
|
||||||
|
const trainLineData = (g as TrainLine).saveData();
|
||||||
|
storage.trainLines.push((trainLineData as TrainLineData).data);
|
||||||
|
} else if (PathLine.Type === g.type) {
|
||||||
|
const pathLineData = (g as PathLine).saveData();
|
||||||
|
storage.pathLines.push((pathLineData as PathLineData).data);
|
||||||
|
} else if (TrainWindow.Type === g.type) {
|
||||||
|
const trainWindowData = (g as TrainWindow).saveData();
|
||||||
|
storage.trainWindows.push((trainWindowData as TrainWindowData).data);
|
||||||
|
} else if (AxleCounting.Type === g.type) {
|
||||||
|
const axleCountingData = (g as AxleCounting).saveData();
|
||||||
|
storage.axleCountings.push((axleCountingData as AxleCountingData).data);
|
||||||
|
} else if (Separator.Type === g.type) {
|
||||||
|
const separatorData = (g as Separator).saveData();
|
||||||
|
storage.separators.push((separatorData as SeparatorData).data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const base64 = fromUint8Array(storage.serialize());
|
||||||
|
console.log('保存数据', storage);
|
||||||
|
// localStorage.setItem(StorageKey, base64);
|
||||||
|
return base64;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { proto: base64 } = await getDraft(id);
|
||||||
|
if (base64) {
|
||||||
|
const storage = graphicData.RtssGraphicStorage.deserialize(
|
||||||
|
toUint8Array(base64)
|
||||||
|
);
|
||||||
|
console.log('加载数据', storage);
|
||||||
|
app.updateCanvas(storage.canvas);
|
||||||
|
const datas: GraphicData[] = [];
|
||||||
|
storage.links.forEach((link) => {
|
||||||
|
datas.push(new LinkData(link));
|
||||||
|
});
|
||||||
|
storage.rects.forEach((rect) => {
|
||||||
|
datas.push(new RectData(rect));
|
||||||
|
});
|
||||||
|
storage.iscsFans.forEach((fan) => {
|
||||||
|
datas.push(new IscsFanData(fan));
|
||||||
|
});
|
||||||
|
storage.Platforms.forEach((platform) => {
|
||||||
|
datas.push(new PlatformData(platform));
|
||||||
|
});
|
||||||
|
storage.stations.forEach((station) => {
|
||||||
|
datas.push(new StationData(station));
|
||||||
|
});
|
||||||
|
storage.train.forEach((train) => {
|
||||||
|
datas.push(new TrainData(train));
|
||||||
|
});
|
||||||
|
storage.turnouts.forEach((turnout) => {
|
||||||
|
datas.push(new TurnoutData(turnout));
|
||||||
|
});
|
||||||
|
storage.signals.forEach((signal) => {
|
||||||
|
datas.push(new SignalData(signal));
|
||||||
|
});
|
||||||
|
storage.runLines.forEach((runLine) => {
|
||||||
|
datas.push(new RunLineData(runLine));
|
||||||
|
});
|
||||||
|
storage.section.forEach((section) => {
|
||||||
|
datas.push(new SectionData(section));
|
||||||
|
});
|
||||||
|
storage.stationLines.forEach((stationLine) => {
|
||||||
|
datas.push(new StationLineData(stationLine));
|
||||||
|
});
|
||||||
|
storage.trainLines.forEach((trainLine) => {
|
||||||
|
datas.push(new TrainLineData(trainLine));
|
||||||
|
});
|
||||||
|
storage.pathLines.forEach((pathLine) => {
|
||||||
|
datas.push(new PathLineData(pathLine));
|
||||||
|
});
|
||||||
|
storage.trainWindows.forEach((trainWindow) => {
|
||||||
|
datas.push(new TrainWindowData(trainWindow));
|
||||||
|
});
|
||||||
|
storage.axleCountings.forEach((axleCounting) => {
|
||||||
|
datas.push(new AxleCountingData(axleCounting));
|
||||||
|
});
|
||||||
|
storage.separators.forEach((separator) => {
|
||||||
|
datas.push(new SeparatorData(separator));
|
||||||
|
});
|
||||||
|
app.loadGraphic(datas);
|
||||||
|
} else {
|
||||||
|
app.loadGraphic([]);
|
||||||
|
}
|
||||||
|
}
|
137
src/drawApp/lineApp.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import {
|
||||||
|
GraphicApp,
|
||||||
|
GraphicData,
|
||||||
|
StompCli,
|
||||||
|
AppWsMsgBroker,
|
||||||
|
GraphicState,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { TrainData, TrainState } from './graphics/TrainInteraction';
|
||||||
|
import { TrainTemplate } from 'src/graphics/train/Train';
|
||||||
|
import {
|
||||||
|
SignalData,
|
||||||
|
SignalOperateInteraction,
|
||||||
|
SignalState,
|
||||||
|
} from './graphics/SignalInteraction';
|
||||||
|
import { SignalTemplate, Signal } from 'src/graphics/signal/Signal';
|
||||||
|
import {
|
||||||
|
PlatformData,
|
||||||
|
PlatformOperateInteraction,
|
||||||
|
PlatformState,
|
||||||
|
} from './graphics/PlatformInteraction';
|
||||||
|
import { PlatformTemplate, Platform } from 'src/graphics/platform/Platform';
|
||||||
|
import { StationData, StationState } from './graphics/StationInteraction';
|
||||||
|
import { StationTemplate } from 'src/graphics/station/Station';
|
||||||
|
import { TurnoutData } from './graphics/TurnoutInteraction';
|
||||||
|
import { TurnoutTemplate } from 'src/graphics/turnout/Turnout';
|
||||||
|
import { SectionData } from './graphics/SectionInteraction';
|
||||||
|
import { SectionTemplate } from 'src/graphics/section/Section';
|
||||||
|
import { getPublishMapInfoByLineId } from 'src/api/PublishApi';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { useLineStore } from 'src/stores/line-store';
|
||||||
|
import { toUint8Array } from 'js-base64';
|
||||||
|
import { getWebsocketUrl } from 'src/configs/UrlManage';
|
||||||
|
import { getJwtToken } from 'src/configs/TokenManage';
|
||||||
|
import { state } from 'src/protos/ws_message';
|
||||||
|
|
||||||
|
let lineApp: GraphicApp | null = null;
|
||||||
|
let msgBroker: AppWsMsgBroker | null = null;
|
||||||
|
|
||||||
|
export function getLineApp(): GraphicApp | null {
|
||||||
|
return lineApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function destroyLineApp(): void {
|
||||||
|
if (lineApp) {
|
||||||
|
lineApp.destroy();
|
||||||
|
lineApp = null;
|
||||||
|
}
|
||||||
|
if (msgBroker) {
|
||||||
|
msgBroker.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initLineApp(dom: HTMLElement): GraphicApp {
|
||||||
|
lineApp = new GraphicApp(dom);
|
||||||
|
const graphicTemplate = [
|
||||||
|
new TrainTemplate(new TrainData(), new TrainState()),
|
||||||
|
new SignalTemplate(new SignalData(), new SignalState()),
|
||||||
|
new PlatformTemplate(new PlatformData(), new PlatformState()),
|
||||||
|
new StationTemplate(new StationData(), new StationState()),
|
||||||
|
new TurnoutTemplate(new TurnoutData()),
|
||||||
|
new SectionTemplate(new SectionData()),
|
||||||
|
];
|
||||||
|
lineApp.registerGraphicTemplates(...graphicTemplate);
|
||||||
|
lineApp.setOptions({
|
||||||
|
mouseToolOptions: {
|
||||||
|
boxSelect: false,
|
||||||
|
viewportDrag: true,
|
||||||
|
wheelZoom: true,
|
||||||
|
},
|
||||||
|
interactiveTypeOptions: {
|
||||||
|
interactiveGraphicTypeIncludes: [Signal.Type, Platform.Type],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
SignalOperateInteraction.init(lineApp);
|
||||||
|
PlatformOperateInteraction.init(lineApp);
|
||||||
|
return lineApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadLineDatas(app: GraphicApp) {
|
||||||
|
const lineStore = useLineStore();
|
||||||
|
const lineId = lineStore.lineId;
|
||||||
|
if (!lineId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { proto: base64, name: lineName } = await getPublishMapInfoByLineId(
|
||||||
|
lineId,
|
||||||
|
'line'
|
||||||
|
);
|
||||||
|
lineStore.setLineName(lineName);
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
storage.stations.forEach((station) => {
|
||||||
|
datas.push(new StationData(station));
|
||||||
|
});
|
||||||
|
storage.train.forEach((train) => {
|
||||||
|
datas.push(new TrainData(train));
|
||||||
|
});
|
||||||
|
storage.turnouts.forEach((turnout) => {
|
||||||
|
datas.push(new TurnoutData(turnout));
|
||||||
|
});
|
||||||
|
storage.signals.forEach((signal) => {
|
||||||
|
datas.push(new SignalData(signal));
|
||||||
|
});
|
||||||
|
storage.section.forEach((section) => {
|
||||||
|
datas.push(new SectionData(section));
|
||||||
|
});
|
||||||
|
await app.loadGraphic(datas);
|
||||||
|
|
||||||
|
StompCli.new({
|
||||||
|
wsUrl: `${getWebsocketUrl()}`,
|
||||||
|
token: getJwtToken() as string,
|
||||||
|
});
|
||||||
|
msgBroker = new AppWsMsgBroker(app);
|
||||||
|
const states: GraphicState[] = [];
|
||||||
|
msgBroker.subscribe({
|
||||||
|
destination: `/queue/line/${lineId}/device`,
|
||||||
|
messageConverter: (message: Uint8Array) => {
|
||||||
|
const storage = state.WsLineMessage.deserialize(message);
|
||||||
|
storage.signal.forEach((item) => {
|
||||||
|
states.push(new SignalState(item));
|
||||||
|
});
|
||||||
|
return states;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
app.loadGraphic([]);
|
||||||
|
}
|
||||||
|
}
|
132
src/drawApp/lineNetApp.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import {
|
||||||
|
GraphicApp,
|
||||||
|
GraphicData,
|
||||||
|
StompCli,
|
||||||
|
AppWsMsgBroker,
|
||||||
|
GraphicState,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { getPublishLineNet } from 'src/api/PublishApi';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { state } from 'src/protos/ws_message';
|
||||||
|
|
||||||
|
import { RunLine, RunLineTemplate } from 'src/graphics/runLine/RunLine';
|
||||||
|
import {
|
||||||
|
RunLineData,
|
||||||
|
RunLineOperateInteraction,
|
||||||
|
} from './graphics/RunLineInteraction';
|
||||||
|
import { PathLineTemplate, PathLine } from 'src/graphics/pathLine/PathLine';
|
||||||
|
import { PathLineData } from './graphics/PathLineInteraction';
|
||||||
|
import {
|
||||||
|
StationLineTemplate,
|
||||||
|
StationLine,
|
||||||
|
} from 'src/graphics/stationLine/StationLine';
|
||||||
|
import { StationLineData } from './graphics/StationLineInteraction';
|
||||||
|
import { ItrainLineTemplate } from 'src/graphics/trainLine/TrainLine';
|
||||||
|
import { TrainLineData, TrainLineState } from './graphics/TrainLineInteraction';
|
||||||
|
import { RectTemplate } from 'src/graphics/rect/Rect';
|
||||||
|
import { RectData } from './graphics/RectInteraction';
|
||||||
|
|
||||||
|
import { useLineNetStore } from 'src/stores/line-net-store';
|
||||||
|
import { toUint8Array } from 'js-base64';
|
||||||
|
import { getWebsocketUrl } from 'src/configs/UrlManage';
|
||||||
|
import { getJwtToken } from 'src/configs/TokenManage';
|
||||||
|
|
||||||
|
let lineNetApp: GraphicApp | null = null;
|
||||||
|
let msgBroker: AppWsMsgBroker | null = null;
|
||||||
|
|
||||||
|
export function getLineNetApp(): GraphicApp | null {
|
||||||
|
return lineNetApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function destroyLineNetApp(): void {
|
||||||
|
if (lineNetApp) {
|
||||||
|
lineNetApp.destroy();
|
||||||
|
lineNetApp = null;
|
||||||
|
}
|
||||||
|
if (msgBroker) {
|
||||||
|
msgBroker.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initLineNetApp(dom: HTMLElement): GraphicApp {
|
||||||
|
lineNetApp = new GraphicApp(dom);
|
||||||
|
const graphicTemplate = [
|
||||||
|
new RunLineTemplate(new RunLineData()),
|
||||||
|
new PathLineTemplate(new PathLineData()),
|
||||||
|
new StationLineTemplate(new StationLineData()),
|
||||||
|
new ItrainLineTemplate(new TrainLineData(), new TrainLineState()),
|
||||||
|
new RectTemplate(new RectData()),
|
||||||
|
];
|
||||||
|
lineNetApp.registerGraphicTemplates(...graphicTemplate);
|
||||||
|
lineNetApp.setOptions({
|
||||||
|
mouseToolOptions: {
|
||||||
|
boxSelect: false,
|
||||||
|
viewportDrag: true,
|
||||||
|
wheelZoom: true,
|
||||||
|
},
|
||||||
|
interactiveTypeOptions: {
|
||||||
|
interactiveGraphicTypeIncludes: [RunLine.Type, StationLine.Type],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
RunLineOperateInteraction.init(lineNetApp);
|
||||||
|
return lineNetApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadLineNetDatas(app: GraphicApp) {
|
||||||
|
const lineNetStore = useLineNetStore();
|
||||||
|
const { proto: base64, name: lineNetName } = await getPublishLineNet();
|
||||||
|
lineNetStore.setLineNetName(lineNetName);
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
storage.pathLines.forEach((pathLine) => {
|
||||||
|
const g = new PathLineData(pathLine);
|
||||||
|
datas.push(g);
|
||||||
|
});
|
||||||
|
storage.stationLines.forEach((stationLine) => {
|
||||||
|
const g = new StationLineData(stationLine);
|
||||||
|
datas.push(g);
|
||||||
|
});
|
||||||
|
storage.trainLines.forEach((trainLine) => {
|
||||||
|
const g = new TrainLineData(trainLine);
|
||||||
|
datas.push(g);
|
||||||
|
});
|
||||||
|
storage.rects.forEach((rect) => {
|
||||||
|
const g = new RectData(rect);
|
||||||
|
datas.push(g);
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
const states: GraphicState[] = [];
|
||||||
|
msgBroker.subscribe({
|
||||||
|
destination: '/queue/lineNet',
|
||||||
|
messageConverter: (message: Uint8Array) => {
|
||||||
|
const storage = state.WsLineNetMessage.deserialize(message);
|
||||||
|
console.log(storage, 'storage');
|
||||||
|
storage.offset.forEach((item) => {
|
||||||
|
states.push(new TrainLineState(item));
|
||||||
|
});
|
||||||
|
return states;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
app.loadGraphic([]);
|
||||||
|
}
|
||||||
|
}
|
9
src/env.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
declare namespace NodeJS {
|
||||||
|
interface ProcessEnv {
|
||||||
|
NODE_ENV: string;
|
||||||
|
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
|
||||||
|
VUE_ROUTER_BASE: string | undefined;
|
||||||
|
}
|
||||||
|
}
|
116
src/graphics/CommonGraphics.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { Graphics } from 'pixi.js';
|
||||||
|
import { calculateMirrorPoint } from 'src/jl-graphic';
|
||||||
|
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||||
|
import { Turnout, TurnoutPort } from './turnout/Turnout';
|
||||||
|
import { Section, SectionPort } from './section/Section';
|
||||||
|
import { TrainWindow } from './trainWindow/TrainWindow';
|
||||||
|
import { AxleCounting } from './axleCounting/AxleCounting';
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param polygon
|
||||||
|
* @param x 箭头顶点x坐标
|
||||||
|
* @param y 箭头顶点y坐标
|
||||||
|
* @param length 箭头长度
|
||||||
|
* @param radius 箭头三角半径
|
||||||
|
* @param lineWidth 箭头线宽
|
||||||
|
* @param mirror 是否镜像翻转 (基于箭头顶点)
|
||||||
|
*/
|
||||||
|
export function drawArrow(
|
||||||
|
polygon: Graphics,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
length: number,
|
||||||
|
radius: number,
|
||||||
|
lineWidth: number,
|
||||||
|
mirror: boolean
|
||||||
|
) {
|
||||||
|
const trianglAcme = { x, y };
|
||||||
|
let triangleP1 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y + Math.cos(Math.PI / 6) * radius,
|
||||||
|
};
|
||||||
|
let triangleP2 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y - Math.cos(Math.PI / 6) * radius,
|
||||||
|
};
|
||||||
|
let lineP1 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y + lineWidth / 2,
|
||||||
|
};
|
||||||
|
let lineP2 = {
|
||||||
|
x: x - length,
|
||||||
|
y: y + lineWidth / 2,
|
||||||
|
};
|
||||||
|
let lineP3 = {
|
||||||
|
x: x - length,
|
||||||
|
y: y - lineWidth / 2,
|
||||||
|
};
|
||||||
|
let lineP4 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y - lineWidth / 2,
|
||||||
|
};
|
||||||
|
if (mirror) {
|
||||||
|
triangleP1 = calculateMirrorPoint(trianglAcme, triangleP1);
|
||||||
|
triangleP2 = calculateMirrorPoint(trianglAcme, triangleP2);
|
||||||
|
lineP1 = calculateMirrorPoint(trianglAcme, lineP1);
|
||||||
|
lineP2 = calculateMirrorPoint(trianglAcme, lineP2);
|
||||||
|
lineP3 = calculateMirrorPoint(trianglAcme, lineP3);
|
||||||
|
lineP4 = calculateMirrorPoint(trianglAcme, lineP4);
|
||||||
|
}
|
||||||
|
polygon.drawPolygon(
|
||||||
|
trianglAcme.x,
|
||||||
|
trianglAcme.y,
|
||||||
|
triangleP1.x,
|
||||||
|
triangleP1.y,
|
||||||
|
lineP1.x,
|
||||||
|
lineP1.y,
|
||||||
|
lineP2.x,
|
||||||
|
lineP2.y,
|
||||||
|
lineP3.x,
|
||||||
|
lineP3.y,
|
||||||
|
lineP4.x,
|
||||||
|
lineP4.y,
|
||||||
|
triangleP2.x,
|
||||||
|
triangleP2.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRelatedRefProto(
|
||||||
|
type: string,
|
||||||
|
id: string,
|
||||||
|
port?: TurnoutPort | SectionPort
|
||||||
|
) {
|
||||||
|
const typeMap = new Map([
|
||||||
|
[Section.Type, graphicData.RelatedRef.DeviceType.Section],
|
||||||
|
[Turnout.Type, graphicData.RelatedRef.DeviceType.Turnout],
|
||||||
|
[TrainWindow.Type, graphicData.RelatedRef.DeviceType.TrainWindow],
|
||||||
|
[AxleCounting.Type, graphicData.RelatedRef.DeviceType.AxleCounting],
|
||||||
|
]);
|
||||||
|
const protoDeviceType = typeMap.get(type);
|
||||||
|
if (protoDeviceType === undefined) throw Error(`输入的type有误: ${type}`);
|
||||||
|
const protoData = new graphicData.RelatedRef({
|
||||||
|
deviceType: protoDeviceType,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
if (port) {
|
||||||
|
if (port === 'A')
|
||||||
|
protoData.devicePort = graphicData.RelatedRef.DevicePort.A;
|
||||||
|
if (port === 'B')
|
||||||
|
protoData.devicePort = graphicData.RelatedRef.DevicePort.B;
|
||||||
|
if (port === 'C')
|
||||||
|
protoData.devicePort = graphicData.RelatedRef.DevicePort.C;
|
||||||
|
}
|
||||||
|
return protoData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function protoPort2Data(port: graphicData.RelatedRef.DevicePort) {
|
||||||
|
if (port === graphicData.RelatedRef.DevicePort.A) return 'A';
|
||||||
|
if (port === graphicData.RelatedRef.DevicePort.B) return 'B';
|
||||||
|
if (port === graphicData.RelatedRef.DevicePort.C) return 'C';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRelatedRefData {
|
||||||
|
deviceType: graphicData.RelatedRef.DeviceType; //关联的设备类型
|
||||||
|
id: string; //关联的设备ID
|
||||||
|
devicePort: graphicData.RelatedRef.DevicePort; //关联的设备端口
|
||||||
|
}
|
116
src/graphics/axleCounting/AxleCounting.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { Color, Container, Graphics } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
GraphicRelationParam,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { IRelatedRefData, protoPort2Data } from '../CommonGraphics';
|
||||||
|
import { KilometerSystem } from '../signal/Signal';
|
||||||
|
|
||||||
|
export interface IAxleCountingData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get kilometerSystem(): KilometerSystem;
|
||||||
|
set kilometerSystem(v: KilometerSystem);
|
||||||
|
get axleCountingRef(): IRelatedRefData[]; //关联的设备
|
||||||
|
set axleCountingRef(ref: IRelatedRefData[]);
|
||||||
|
clone(): IAxleCountingData;
|
||||||
|
copyFrom(data: IAxleCountingData): void;
|
||||||
|
eq(other: IAxleCountingData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AxleCountingConsts = {
|
||||||
|
radius: 6,
|
||||||
|
borderWidth: 1,
|
||||||
|
circleColorBlue: '0x08F80D',
|
||||||
|
codeColor: '0xF48815',
|
||||||
|
codeFontSize: 22,
|
||||||
|
codeOffsetY: 30,
|
||||||
|
kilometerCodeColor: '0xFFFFFF',
|
||||||
|
kilometerCodeFontSize: 14,
|
||||||
|
kilometerCodeOffsetY: 95,
|
||||||
|
offsetSection: 50,
|
||||||
|
};
|
||||||
|
class TwoCircleGraphic extends Container {
|
||||||
|
circleA: Graphics = new Graphics();
|
||||||
|
circleB: Graphics = new Graphics();
|
||||||
|
line: Graphics = new Graphics();
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.addChild(this.circleA);
|
||||||
|
this.addChild(this.circleB);
|
||||||
|
this.addChild(this.line);
|
||||||
|
}
|
||||||
|
draw(): void {
|
||||||
|
this.drawCircle(this.circleA);
|
||||||
|
this.drawCircle(this.circleB);
|
||||||
|
this.circleA.position.set(-12, 0);
|
||||||
|
this.circleB.position.set(12, 0);
|
||||||
|
this.line.clear();
|
||||||
|
this.line.lineStyle(1, new Color(AxleCountingConsts.circleColorBlue));
|
||||||
|
this.line.moveTo(-24, 0);
|
||||||
|
this.line.lineTo(24, 0);
|
||||||
|
}
|
||||||
|
drawCircle(circle: Graphics): void {
|
||||||
|
circle.clear();
|
||||||
|
circle.lineStyle(
|
||||||
|
AxleCountingConsts.borderWidth,
|
||||||
|
new Color(AxleCountingConsts.circleColorBlue)
|
||||||
|
);
|
||||||
|
circle.beginFill(AxleCountingConsts.circleColorBlue, 1);
|
||||||
|
circle.drawCircle(0, 0, AxleCountingConsts.radius);
|
||||||
|
circle.endFill;
|
||||||
|
}
|
||||||
|
clear(): void {
|
||||||
|
this.circleA.clear();
|
||||||
|
this.circleB.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class AxleCounting extends JlGraphic {
|
||||||
|
static Type = 'AxleCounting';
|
||||||
|
twoCircle: TwoCircleGraphic = new TwoCircleGraphic();
|
||||||
|
kilometerGraph: VectorText = new VectorText(''); //公里标
|
||||||
|
direction: number;
|
||||||
|
constructor(direction: number) {
|
||||||
|
super(AxleCounting.Type);
|
||||||
|
this.addChild(this.twoCircle);
|
||||||
|
this.addChild(this.kilometerGraph);
|
||||||
|
this.kilometerGraph.name = 'kilometer';
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IAxleCountingData {
|
||||||
|
return this.getDatas<IAxleCountingData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
this.twoCircle.draw();
|
||||||
|
}
|
||||||
|
loadRelations(): void {
|
||||||
|
if (this.datas.axleCountingRef.length) {
|
||||||
|
this.datas.axleCountingRef.forEach((device) => {
|
||||||
|
this.relationManage.addRelation(
|
||||||
|
new GraphicRelationParam(this, 'A'),
|
||||||
|
new GraphicRelationParam(
|
||||||
|
this.queryStore.queryById(device.id),
|
||||||
|
protoPort2Data(device.devicePort)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AxleCountingTemplate extends JlGraphicTemplate<AxleCounting> {
|
||||||
|
constructor(dataTemplate: IAxleCountingData) {
|
||||||
|
super(AxleCounting.Type, {
|
||||||
|
dataTemplate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
new(): AxleCounting {
|
||||||
|
const axleCounting = new AxleCounting(1);
|
||||||
|
axleCounting.loadData(this.datas);
|
||||||
|
return axleCounting;
|
||||||
|
}
|
||||||
|
}
|
394
src/graphics/axleCounting/AxleCountingDrawAssistant.ts
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
import { FederatedPointerEvent, IPoint, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
AbsorbableLine,
|
||||||
|
AbsorbablePosition,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicIdGenerator,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
distance2,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IAxleCountingData,
|
||||||
|
AxleCounting,
|
||||||
|
AxleCountingTemplate,
|
||||||
|
AxleCountingConsts,
|
||||||
|
} from './AxleCounting';
|
||||||
|
import { Section, SectionPort, SectionType } from '../section/Section';
|
||||||
|
import { Turnout, TurnoutPort } from '../turnout/Turnout';
|
||||||
|
import { IRelatedRefData, createRelatedRefProto } from '../CommonGraphics';
|
||||||
|
import { Signal } from '../signal/Signal';
|
||||||
|
|
||||||
|
export interface IAxleCountingDrawOptions {
|
||||||
|
newData: () => IAxleCountingData;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DevicePort {
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
C = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DevicePortN {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AxleCountingDraw extends GraphicDrawAssistant<
|
||||||
|
AxleCountingTemplate,
|
||||||
|
IAxleCountingData
|
||||||
|
> {
|
||||||
|
codeGraph: AxleCounting;
|
||||||
|
constructor(app: JlDrawApp, template: AxleCountingTemplate) {
|
||||||
|
super(app, template, 'sym_o_circle', '不展示');
|
||||||
|
this.codeGraph = this.graphicTemplate.new();
|
||||||
|
this.container.addChild(this.codeGraph);
|
||||||
|
AxleCountingInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
this.codeGraph.loadData(this.graphicTemplate.datas);
|
||||||
|
this.codeGraph.doRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
//this.codeGraph.destroy();
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
this.container.position.copyFrom(this.toCanvasCoordinates(e.global));
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
this.container.position.copyFrom(p);
|
||||||
|
}
|
||||||
|
prepareData(data: IAxleCountingData): boolean {
|
||||||
|
data.transform = this.container.saveTransform();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
draw(
|
||||||
|
ps: IPoint,
|
||||||
|
direction: number,
|
||||||
|
graphic: Section | Turnout,
|
||||||
|
port: TurnoutPort | SectionPort,
|
||||||
|
map: Map<string, number>,
|
||||||
|
reftype: string,
|
||||||
|
refGraphic: Section | Turnout,
|
||||||
|
refPort: TurnoutPort | SectionPort
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
graphic.type == 'Turnout' &&
|
||||||
|
reftype == 'Turnout' &&
|
||||||
|
port == TurnoutPort.B &&
|
||||||
|
refPort == TurnoutPort.B
|
||||||
|
) {
|
||||||
|
const points = (graphic as Turnout).getPortPoints();
|
||||||
|
const portPs = graphic.localToCanvasPoints(points[1][0])[0];
|
||||||
|
let hasSingle = false;
|
||||||
|
const singles = this.app.queryStore.queryByType<Signal>(Signal.Type);
|
||||||
|
singles.forEach((single) => {
|
||||||
|
if (distance2(portPs, single.position) < 50) {
|
||||||
|
hasSingle = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!hasSingle) {
|
||||||
|
map.set(`${graphic.id}-${port}`, 1);
|
||||||
|
map.set(`${refGraphic.id}-${refPort}`, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!map.has(`${refGraphic.id}-${refPort}`) &&
|
||||||
|
!map.has(`${graphic.id}-${port}`)
|
||||||
|
) {
|
||||||
|
map.set(`${graphic.id}-${port}`, 1);
|
||||||
|
map.set(`${refGraphic.id}-${refPort}`, 1);
|
||||||
|
const refData1 = createRelatedRefProto(reftype, refGraphic.id, refPort);
|
||||||
|
const refData2 = createRelatedRefProto(graphic.type, graphic.id, port);
|
||||||
|
const axleCounting = new AxleCounting(direction);
|
||||||
|
axleCounting.loadData(this.graphicTemplate.datas);
|
||||||
|
if (graphic.type == 'Turnout') {
|
||||||
|
axleCounting.position.set(ps.x, ps.y);
|
||||||
|
} else {
|
||||||
|
axleCounting.position.set(
|
||||||
|
ps.x,
|
||||||
|
ps.y - AxleCountingConsts.offsetSection * direction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
axleCounting.id = GraphicIdGenerator.next();
|
||||||
|
axleCounting.datas.axleCountingRef = [refData2, refData1];
|
||||||
|
axleCounting.datas.code = `${graphic.datas.code}-${port}+${refGraphic.datas.code}-${refPort}`;
|
||||||
|
this.storeGraphic(axleCounting);
|
||||||
|
axleCounting.loadRelations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawAdd(
|
||||||
|
ps: IPoint,
|
||||||
|
direction: number,
|
||||||
|
graphic: Section | Turnout,
|
||||||
|
port: TurnoutPort | SectionPort,
|
||||||
|
map: Map<string, number>
|
||||||
|
) {
|
||||||
|
if (!map.has(`${graphic.id}-${port}`)) {
|
||||||
|
map.set(`${graphic.id}-${port}`, 1);
|
||||||
|
const refData = createRelatedRefProto(graphic.type, graphic.id, port);
|
||||||
|
const axleCounting = new AxleCounting(direction);
|
||||||
|
axleCounting.loadData(this.graphicTemplate.datas);
|
||||||
|
if (graphic.type == 'Turnout') {
|
||||||
|
axleCounting.position.set(ps.x, ps.y);
|
||||||
|
} else {
|
||||||
|
axleCounting.position.set(
|
||||||
|
ps.x,
|
||||||
|
ps.y - AxleCountingConsts.offsetSection * direction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
axleCounting.id = GraphicIdGenerator.next();
|
||||||
|
axleCounting.datas.axleCountingRef = [refData];
|
||||||
|
axleCounting.datas.code = `${graphic.datas.code}-${port}`;
|
||||||
|
this.storeGraphic(axleCounting);
|
||||||
|
axleCounting.loadRelations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oneGenerates(height: Point) {
|
||||||
|
const map = new Map();
|
||||||
|
const axleCountings = this.app.queryStore.queryByType<AxleCounting>(
|
||||||
|
AxleCounting.Type
|
||||||
|
);
|
||||||
|
const axleCountingRefs: IRelatedRefData[] = [];
|
||||||
|
axleCountings.forEach((axleCounting) => {
|
||||||
|
axleCountingRefs.push(...axleCounting.datas.axleCountingRef);
|
||||||
|
});
|
||||||
|
axleCountingRefs.forEach((axleCountingRef) => {
|
||||||
|
map.set(
|
||||||
|
`${axleCountingRef.id}-${DevicePortN[axleCountingRef.devicePort]}`,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
});
|
||||||
|
//由区段生成计轴--区段和区段
|
||||||
|
const sections = this.app.queryStore
|
||||||
|
.queryByType<Section>(Section.Type)
|
||||||
|
.filter((section) => {
|
||||||
|
return (
|
||||||
|
section.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
section,
|
||||||
|
AxleCounting.Type
|
||||||
|
).length < 2
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
sections.forEach((section) => {
|
||||||
|
const sectionRelations =
|
||||||
|
section.relationManage.getRelationsOfGraphic(section);
|
||||||
|
const ps = section.localToCanvasPoint(section.getStartPoint());
|
||||||
|
const pe = section.localToCanvasPoint(section.getEndPoint());
|
||||||
|
sectionRelations.forEach((relation) => {
|
||||||
|
const port = relation.getRelationParam(section).param;
|
||||||
|
const refDevice = relation.getOtherGraphic<Section>(section);
|
||||||
|
const refDevicePort = relation.getOtherRelationParam(section).param;
|
||||||
|
let direction = 1;
|
||||||
|
let axleCountingPs = ps;
|
||||||
|
if (port == 'B') {
|
||||||
|
axleCountingPs = pe;
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
direction,
|
||||||
|
section,
|
||||||
|
port,
|
||||||
|
map,
|
||||||
|
refDevice.type,
|
||||||
|
refDevice,
|
||||||
|
refDevicePort
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//由区段生成计轴--单独区段
|
||||||
|
sections.forEach((section) => {
|
||||||
|
const axleCountingRelations =
|
||||||
|
section.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
section,
|
||||||
|
AxleCounting.Type
|
||||||
|
);
|
||||||
|
const ps = section.localToCanvasPoint(section.getStartPoint());
|
||||||
|
const pe = section.localToCanvasPoint(section.getEndPoint());
|
||||||
|
if (axleCountingRelations.length < 2) {
|
||||||
|
axleCountingRelations.forEach((relation) => {
|
||||||
|
const port = relation.getRelationParam(section).param;
|
||||||
|
let addPort = SectionPort.A;
|
||||||
|
let direction = 1;
|
||||||
|
let axleCountingPs = ps;
|
||||||
|
if (port == 'A') {
|
||||||
|
axleCountingPs = pe;
|
||||||
|
addPort = SectionPort.B;
|
||||||
|
}
|
||||||
|
if (axleCountingPs.y > height.y) {
|
||||||
|
direction = -1;
|
||||||
|
}
|
||||||
|
if (axleCountingPs.y > height.y) {
|
||||||
|
direction = -1;
|
||||||
|
}
|
||||||
|
this.drawAdd(axleCountingPs, direction, section, addPort, map);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//由道岔生成计轴--道岔和道岔
|
||||||
|
const turnouts = this.app.queryStore
|
||||||
|
.queryByType<Turnout>(Turnout.Type)
|
||||||
|
.filter((turnout) => {
|
||||||
|
return (
|
||||||
|
turnout.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
turnout,
|
||||||
|
AxleCounting.Type
|
||||||
|
).length < 3
|
||||||
|
);
|
||||||
|
});
|
||||||
|
turnouts.forEach((turnout) => {
|
||||||
|
const points = turnout.getPortPoints();
|
||||||
|
//道岔关联的道岔
|
||||||
|
const turnoutRelations =
|
||||||
|
turnout.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
turnout,
|
||||||
|
Turnout.Type
|
||||||
|
);
|
||||||
|
turnoutRelations.forEach((relation) => {
|
||||||
|
const port = relation.getRelationParam(turnout).param;
|
||||||
|
const portIndex = DevicePort[port] as unknown as number;
|
||||||
|
const refTurnout = relation.getOtherGraphic<Turnout>(turnout);
|
||||||
|
const refTurnoutPort = relation.getOtherRelationParam(turnout).param;
|
||||||
|
const portPs = turnout.localToCanvasPoints(...points[portIndex])[
|
||||||
|
points[portIndex].length - 1
|
||||||
|
];
|
||||||
|
this.draw(
|
||||||
|
portPs,
|
||||||
|
1,
|
||||||
|
turnout,
|
||||||
|
port,
|
||||||
|
map,
|
||||||
|
Turnout.Type,
|
||||||
|
refTurnout,
|
||||||
|
refTurnoutPort
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//由道岔生成计轴--单独道岔
|
||||||
|
turnouts.forEach((turnout) => {
|
||||||
|
const axleCountingRelations =
|
||||||
|
turnout.relationManage.getRelationsOfGraphicAndOtherType(
|
||||||
|
turnout,
|
||||||
|
AxleCounting.Type
|
||||||
|
);
|
||||||
|
const points = turnout.getPortPoints();
|
||||||
|
const turnoutPort = [TurnoutPort.A, TurnoutPort.B, TurnoutPort.C];
|
||||||
|
if (axleCountingRelations.length == 1) {
|
||||||
|
const port = axleCountingRelations[0].getRelationParam(turnout).param;
|
||||||
|
const otherPort = turnoutPort.filter((p) => {
|
||||||
|
return p !== port && p;
|
||||||
|
});
|
||||||
|
otherPort.forEach((port) => {
|
||||||
|
const portIndex = DevicePort[port] as unknown as number;
|
||||||
|
const axleCountingPs1 = turnout.localToCanvasPoints(
|
||||||
|
...points[portIndex]
|
||||||
|
)[points[portIndex].length - 1];
|
||||||
|
this.drawAdd(axleCountingPs1, 1, turnout, port, map);
|
||||||
|
});
|
||||||
|
} else if (axleCountingRelations.length == 2) {
|
||||||
|
const port = axleCountingRelations.map(
|
||||||
|
(item) => item.getRelationParam(turnout).param
|
||||||
|
);
|
||||||
|
const otherPort = turnoutPort.filter((p) => {
|
||||||
|
return p !== port[0] && p !== port[1] && p;
|
||||||
|
});
|
||||||
|
if (otherPort.length) {
|
||||||
|
const portIndex = DevicePort[otherPort[0]] as unknown as number;
|
||||||
|
const axleCountingPs1 = turnout.localToCanvasPoints(
|
||||||
|
...points[portIndex]
|
||||||
|
)[points[portIndex].length - 1];
|
||||||
|
this.drawAdd(axleCountingPs1, 1, turnout, otherPort[0], map);
|
||||||
|
}
|
||||||
|
} else if (axleCountingRelations.length == 0) {
|
||||||
|
const axleCountingPsA = turnout.localToCanvasPoints(points[0][0])[0];
|
||||||
|
const axleCountingPsC = turnout.localToCanvasPoints(points[2][0])[0];
|
||||||
|
this.drawAdd(axleCountingPsA, 1, turnout, TurnoutPort.A, map);
|
||||||
|
this.drawAdd(axleCountingPsC, 1, turnout, TurnoutPort.C, map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAbsorbablePositions(
|
||||||
|
axleCounting: AxleCounting
|
||||||
|
): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
const axleCountings = axleCounting.queryStore.queryByType<AxleCounting>(
|
||||||
|
AxleCounting.Type
|
||||||
|
);
|
||||||
|
const { width } = axleCounting.getGraphicApp().canvas;
|
||||||
|
axleCountings.forEach((other) => {
|
||||||
|
if (other.id == axleCounting.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ps = other.datas.transform.position;
|
||||||
|
const xs = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y });
|
||||||
|
aps.push(xs);
|
||||||
|
});
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AxleCountingInteraction extends GraphicInteractionPlugin<AxleCounting> {
|
||||||
|
static Name = 'AxleCounting_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(AxleCountingInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new AxleCountingInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): AxleCounting[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === AxleCounting.Type)
|
||||||
|
.map((g) => g as AxleCounting);
|
||||||
|
}
|
||||||
|
bind(g: AxleCounting): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
g.kilometerGraph.eventMode = 'static';
|
||||||
|
g.kilometerGraph.cursor = 'pointer';
|
||||||
|
g.kilometerGraph.draggable = true;
|
||||||
|
g.kilometerGraph.selectable = true;
|
||||||
|
g.kilometerGraph.transformSave = true;
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
unbind(g: AxleCounting): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
g.kilometerGraph.eventMode = 'none';
|
||||||
|
g.kilometerGraph.draggable = false;
|
||||||
|
g.kilometerGraph.selectable = false;
|
||||||
|
g.kilometerGraph.transformSave = false;
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
onSelected(): void {
|
||||||
|
const AxleCounting = this.app.selectedGraphics[0] as AxleCounting;
|
||||||
|
this.app.setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(AxleCounting),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
125
src/graphics/iscs-fan/IscsFan.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import {
|
||||||
|
GraphicAnimation,
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import ISCS_FAN_Assets from './iscs-fan-spritesheet.png';
|
||||||
|
import ISCS_FAN_JSON from './iscs-fan-data.json';
|
||||||
|
|
||||||
|
import { Assets, Sprite, Spritesheet, Texture } from 'pixi.js';
|
||||||
|
|
||||||
|
interface FanTextures {
|
||||||
|
border: Texture;
|
||||||
|
blue: Texture;
|
||||||
|
gray: Texture;
|
||||||
|
green: Texture;
|
||||||
|
red: Texture;
|
||||||
|
yellow: Texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IIscsFanData extends GraphicData {
|
||||||
|
get code(): string;
|
||||||
|
set code(v: string);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IscsFan extends JlGraphic {
|
||||||
|
static Type = 'IscsFan';
|
||||||
|
_border: Sprite;
|
||||||
|
_fan: Sprite;
|
||||||
|
fanTextures: FanTextures;
|
||||||
|
__state = 0;
|
||||||
|
|
||||||
|
constructor(fanTextures: FanTextures) {
|
||||||
|
super(IscsFan.Type);
|
||||||
|
this.fanTextures = fanTextures;
|
||||||
|
this._border = new Sprite();
|
||||||
|
this._border.texture = this.fanTextures.border;
|
||||||
|
this._border.anchor.set(0.5);
|
||||||
|
this._fan = new Sprite();
|
||||||
|
this._fan.texture = this.fanTextures.gray;
|
||||||
|
this._fan.anchor.set(0.5);
|
||||||
|
this.addChild(this._border);
|
||||||
|
this.addChild(this._fan);
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
if (this.__state === 0) {
|
||||||
|
// 停止
|
||||||
|
this.stopFanRun();
|
||||||
|
this._fan.rotation = 0;
|
||||||
|
this._fan.texture = this.fanTextures.gray;
|
||||||
|
} else if (this.__state === 1) {
|
||||||
|
// 正常运行
|
||||||
|
this._fan.texture = this.fanTextures.green;
|
||||||
|
// 动画
|
||||||
|
this.initFanRun();
|
||||||
|
} else if (this.__state === 2) {
|
||||||
|
// 报警运行
|
||||||
|
this._fan.texture = this.fanTextures.yellow;
|
||||||
|
// 动画
|
||||||
|
this.initFanRun();
|
||||||
|
} else if (this.__state === 3) {
|
||||||
|
// 故障
|
||||||
|
this.stopFanRun();
|
||||||
|
this._fan.rotation = 0;
|
||||||
|
this._fan.texture = this.fanTextures.red;
|
||||||
|
} else if (this.__state === 4) {
|
||||||
|
// 通信故障
|
||||||
|
// 停止
|
||||||
|
this.stopFanRun();
|
||||||
|
this._fan.rotation = 0;
|
||||||
|
this._fan.texture = this.fanTextures.blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initFanRun() {
|
||||||
|
// 动画
|
||||||
|
const name = 'fan_run';
|
||||||
|
let fanRun = this.animation(name);
|
||||||
|
if (!fanRun) {
|
||||||
|
fanRun = GraphicAnimation.init({
|
||||||
|
name: 'fan_run',
|
||||||
|
run: (dt: number) => {
|
||||||
|
this._fan.angle = (this._fan.angle + dt) % 360;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.addAnimation(fanRun);
|
||||||
|
}
|
||||||
|
const speed = Math.round(Math.random() * 10) + 1;
|
||||||
|
fanRun.xSpeed = speed;
|
||||||
|
fanRun.resume();
|
||||||
|
}
|
||||||
|
stopFanRun() {
|
||||||
|
const name = 'fan_run';
|
||||||
|
const fanRun = this.animation(name);
|
||||||
|
if (fanRun) {
|
||||||
|
fanRun.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IscsFanTemplate extends JlGraphicTemplate<IscsFan> {
|
||||||
|
fanTextures?: FanTextures;
|
||||||
|
constructor() {
|
||||||
|
super(IscsFan.Type, {});
|
||||||
|
}
|
||||||
|
new(): IscsFan {
|
||||||
|
if (this.fanTextures) {
|
||||||
|
return new IscsFan(this.fanTextures);
|
||||||
|
}
|
||||||
|
throw new Error('资源未加载/加载失败');
|
||||||
|
}
|
||||||
|
async loadAssets(): Promise<FanTextures> {
|
||||||
|
const texture = await Assets.load(ISCS_FAN_Assets);
|
||||||
|
const iscsFanSheet = new Spritesheet(texture, ISCS_FAN_JSON);
|
||||||
|
const result = await iscsFanSheet.parse();
|
||||||
|
this.fanTextures = {
|
||||||
|
border: result['fan-border.png'],
|
||||||
|
blue: result['fan-blue.png'],
|
||||||
|
gray: result['fan-gray.png'],
|
||||||
|
green: result['fan-green.png'],
|
||||||
|
red: result['fan-red.png'],
|
||||||
|
yellow: result['fan-yellow.png'],
|
||||||
|
};
|
||||||
|
return this.fanTextures as FanTextures;
|
||||||
|
}
|
||||||
|
}
|
77
src/graphics/iscs-fan/IscsFanDrawAssistant.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { FederatedMouseEvent, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { IIscsFanData, IscsFan, IscsFanTemplate } from './IscsFan';
|
||||||
|
|
||||||
|
export class IscsFanDraw extends GraphicDrawAssistant<
|
||||||
|
IscsFanTemplate,
|
||||||
|
IIscsFanData
|
||||||
|
> {
|
||||||
|
_iscsFan: IscsFan | null = null;
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
const template = new IscsFanTemplate();
|
||||||
|
super(app, template, 'svguse:../drawIcon.svg#icon-fans', '风机');
|
||||||
|
IscsFanInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
if (!this._iscsFan) {
|
||||||
|
this._iscsFan = this.graphicTemplate.new();
|
||||||
|
this.container.addChild(this._iscsFan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get iscsFan(): IscsFan {
|
||||||
|
if (!this._iscsFan) {
|
||||||
|
throw new Error('风机绘制逻辑异常');
|
||||||
|
}
|
||||||
|
return this._iscsFan;
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(cp: Point): void {
|
||||||
|
this.iscsFan.position.copyFrom(cp);
|
||||||
|
}
|
||||||
|
onLeftUp(e: FederatedMouseEvent): void {
|
||||||
|
this.iscsFan.position.copyFrom(this.toCanvasCoordinates(e.global));
|
||||||
|
this.createAndStore(false);
|
||||||
|
}
|
||||||
|
prepareData(data: IIscsFanData): boolean {
|
||||||
|
data.transform = this.iscsFan.saveTransform();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
onEsc(): void {
|
||||||
|
this.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IscsFanInteraction extends GraphicInteractionPlugin<IscsFan> {
|
||||||
|
static Name = 'iscs_fan_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(IscsFanInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new IscsFanInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): IscsFan[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === IscsFan.Type)
|
||||||
|
.map((g) => g as IscsFan);
|
||||||
|
}
|
||||||
|
bind(g: IscsFan): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
}
|
||||||
|
unbind(g: IscsFan): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
66
src/graphics/iscs-fan/iscs-fan-data.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{"frames": {
|
||||||
|
|
||||||
|
"fan-blue.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-border.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":41,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-gray.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":82,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-green.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":123,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-red.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":164,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
},
|
||||||
|
"fan-yellow.png":
|
||||||
|
{
|
||||||
|
"frame": {"x":205,"y":0,"w":41,"h":41},
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": {"x":0,"y":0,"w":41,"h":41},
|
||||||
|
"sourceSize": {"w":41,"h":41},
|
||||||
|
"anchor": {"x":0.5,"y":0.5}
|
||||||
|
}},
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "1.1",
|
||||||
|
"image": "test-iscs-fan.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {"w":246,"h":41},
|
||||||
|
"scale": "1",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:e7620bd2d73cc0b3e2deea9704e7eefc:f129a1d9e4b9ba57720b3861c22b155b:eb2d421f7759984b7713aa4aa5354134$"
|
||||||
|
}
|
||||||
|
}
|
BIN
src/graphics/iscs-fan/iscs-fan-spritesheet.png
Normal file
After Width: | Height: | Size: 16 KiB |
102
src/graphics/link/Link.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { Color, Graphics, IPointData } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
convertToBezierParams,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { ILineGraphic } from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
|
||||||
|
export interface ILinkData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get curve(): boolean; // 是否曲线
|
||||||
|
set curve(v: boolean);
|
||||||
|
get segmentsCount(): number; // 曲线分段数
|
||||||
|
set segmentsCount(v: number);
|
||||||
|
get points(): IPointData[]; // 线坐标点
|
||||||
|
set points(points: IPointData[]);
|
||||||
|
get lineWidth(): number; // 线宽
|
||||||
|
set lineWidth(v: number);
|
||||||
|
get lineColor(): string; // 线色
|
||||||
|
set lineColor(v: string);
|
||||||
|
clone(): ILinkData;
|
||||||
|
copyFrom(data: ILinkData): void;
|
||||||
|
eq(other: ILinkData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Link extends JlGraphic implements ILineGraphic {
|
||||||
|
static Type = 'Link';
|
||||||
|
lineGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super(Link.Type);
|
||||||
|
this.lineGraphic = new Graphics();
|
||||||
|
this.addChild(this.lineGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): ILinkData {
|
||||||
|
return this.getDatas<ILinkData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
if (this.datas.points.length < 2) {
|
||||||
|
throw new Error('Link坐标数据异常');
|
||||||
|
}
|
||||||
|
this.lineGraphic.clear();
|
||||||
|
this.lineGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColor)
|
||||||
|
);
|
||||||
|
if (this.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
const bps = convertToBezierParams(this.datas.points);
|
||||||
|
bps.forEach((bp) => {
|
||||||
|
this.lineGraphic.drawBezierCurve(
|
||||||
|
bp.p1,
|
||||||
|
bp.p2,
|
||||||
|
bp.cp1,
|
||||||
|
bp.cp2,
|
||||||
|
this.datas.segmentsCount
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
const start = this.getStartPoint();
|
||||||
|
this.lineGraphic.moveTo(start.x, start.y);
|
||||||
|
for (let i = 0; i < this.datas.points.length; i++) {
|
||||||
|
const p = this.datas.points[i];
|
||||||
|
this.lineGraphic.lineTo(p.x, p.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get linePoints(): IPointData[] {
|
||||||
|
return this.datas.points;
|
||||||
|
}
|
||||||
|
set linePoints(points: IPointData[]) {
|
||||||
|
const old = this.datas.clone();
|
||||||
|
old.points = points;
|
||||||
|
this.updateData(old);
|
||||||
|
}
|
||||||
|
getStartPoint(): IPointData {
|
||||||
|
return this.datas.points[0];
|
||||||
|
}
|
||||||
|
getEndPoint(): IPointData {
|
||||||
|
return this.datas.points[this.datas.points.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkTemplate extends JlGraphicTemplate<Link> {
|
||||||
|
curve: boolean;
|
||||||
|
lineWidth: number;
|
||||||
|
lineColor: string;
|
||||||
|
segmentsCount: number;
|
||||||
|
constructor() {
|
||||||
|
super(Link.Type, {});
|
||||||
|
this.lineWidth = 2;
|
||||||
|
this.lineColor = '#000000';
|
||||||
|
this.curve = false;
|
||||||
|
this.segmentsCount = 10;
|
||||||
|
}
|
||||||
|
new(): Link {
|
||||||
|
return new Link();
|
||||||
|
}
|
||||||
|
}
|
336
src/graphics/link/LinkDrawAssistant.ts
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
import {
|
||||||
|
Color,
|
||||||
|
DisplayObject,
|
||||||
|
FederatedMouseEvent,
|
||||||
|
FederatedPointerEvent,
|
||||||
|
Graphics,
|
||||||
|
IHitArea,
|
||||||
|
Point,
|
||||||
|
} from 'pixi.js';
|
||||||
|
import {
|
||||||
|
AbsorbablePosition,
|
||||||
|
DraggablePoint,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicTransformEvent,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
KeyListener,
|
||||||
|
calculateMirrorPoint,
|
||||||
|
convertToBezierParams,
|
||||||
|
linePoint,
|
||||||
|
pointPolygon,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import AbsorbablePoint, {
|
||||||
|
AbsorbableCircle,
|
||||||
|
} from 'src/jl-graphic/graphic/AbsorbablePosition';
|
||||||
|
import {
|
||||||
|
BezierCurveEditPlugin,
|
||||||
|
ILineGraphic,
|
||||||
|
PolylineEditPlugin,
|
||||||
|
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
import { ILinkData, Link, LinkTemplate } from './Link';
|
||||||
|
|
||||||
|
export interface ILinkDrawOptions {
|
||||||
|
newData: () => ILinkData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkDraw extends GraphicDrawAssistant<LinkTemplate, ILinkData> {
|
||||||
|
points: Point[] = [];
|
||||||
|
graphic: Graphics = new Graphics();
|
||||||
|
|
||||||
|
// 快捷切曲线绘制
|
||||||
|
keyqListener: KeyListener = new KeyListener({
|
||||||
|
value: 'KeyQ',
|
||||||
|
global: true,
|
||||||
|
onPress: () => {
|
||||||
|
if (this.points.length == 0) {
|
||||||
|
this.graphicTemplate.curve = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 快捷切直线绘制
|
||||||
|
keyzListener: KeyListener = new KeyListener({
|
||||||
|
value: 'KeyZ',
|
||||||
|
global: true,
|
||||||
|
onPress: () => {
|
||||||
|
if (this.points.length == 0) {
|
||||||
|
this.graphicTemplate.curve = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(app, new LinkTemplate(), 'sym_o_horizontal_rule', '轨道Link');
|
||||||
|
this.container.addChild(this.graphic);
|
||||||
|
this.graphicTemplate.curve = true;
|
||||||
|
|
||||||
|
LinkPointsEditPlugin.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
this.app.addKeyboardListener(this.keyqListener, this.keyzListener);
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
this.app.removeKeyboardListener(this.keyqListener, this.keyzListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.points = [];
|
||||||
|
this.graphic.clear();
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
if (this.graphicTemplate.curve) {
|
||||||
|
if (this.points.length == 0) {
|
||||||
|
this.points.push(p);
|
||||||
|
} else {
|
||||||
|
this.points.push(p, p.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.points.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onLeftUp(e: FederatedMouseEvent): void {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
if (template.curve) {
|
||||||
|
const mp = this.toCanvasCoordinates(e.global);
|
||||||
|
if (this.points.length == 1) {
|
||||||
|
this.points.push(new Point(mp.x, mp.y));
|
||||||
|
} else if (this.points.length > 1) {
|
||||||
|
const cp2 = this.points[this.points.length - 2];
|
||||||
|
const p = this.points[this.points.length - 1];
|
||||||
|
cp2.copyFrom(calculateMirrorPoint(p, mp));
|
||||||
|
this.points.push(mp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
if (this.points.length < 1) return;
|
||||||
|
this.graphic.clear();
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
this.graphic.lineStyle(template.lineWidth, new Color(template.lineColor));
|
||||||
|
const ps = [...this.points];
|
||||||
|
if (template.curve) {
|
||||||
|
// 曲线
|
||||||
|
if (ps.length == 1) {
|
||||||
|
this.graphic.moveTo(ps[0].x, ps[0].y);
|
||||||
|
this.graphic.lineTo(p.x, p.y);
|
||||||
|
} else {
|
||||||
|
if ((ps.length + 1) % 3 == 0) {
|
||||||
|
ps.push(p.clone(), p.clone());
|
||||||
|
} else {
|
||||||
|
const cp = ps[ps.length - 2];
|
||||||
|
const p1 = ps[ps.length - 1];
|
||||||
|
const mp = calculateMirrorPoint(p1, p);
|
||||||
|
cp.copyFrom(mp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bps = convertToBezierParams(ps);
|
||||||
|
bps.forEach((bp) =>
|
||||||
|
this.graphic.drawBezierCurve(
|
||||||
|
bp.p1,
|
||||||
|
bp.p2,
|
||||||
|
bp.cp1,
|
||||||
|
bp.cp2,
|
||||||
|
template.segmentsCount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ps.push(p);
|
||||||
|
// 直线
|
||||||
|
this.graphic.moveTo(ps[0].x, ps[0].y);
|
||||||
|
for (let i = 1; i < ps.length; i++) {
|
||||||
|
const p = ps[i];
|
||||||
|
this.graphic.lineTo(p.x, p.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prepareData(data: ILinkData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
if (
|
||||||
|
(!template.curve && this.points.length < 2) ||
|
||||||
|
(template.curve && this.points.length < 4)
|
||||||
|
) {
|
||||||
|
console.log('Link绘制因点不够取消绘制');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (template.curve) {
|
||||||
|
this.points.pop();
|
||||||
|
}
|
||||||
|
data.curve = template.curve;
|
||||||
|
data.segmentsCount = template.segmentsCount;
|
||||||
|
data.points = this.points;
|
||||||
|
data.lineWidth = template.lineWidth;
|
||||||
|
data.lineColor = template.lineColor;
|
||||||
|
data.segmentsCount = template.segmentsCount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkGraphicHitArea implements IHitArea {
|
||||||
|
link: Link;
|
||||||
|
constructor(link: Link) {
|
||||||
|
this.link = link;
|
||||||
|
}
|
||||||
|
contains(x: number, y: number): boolean {
|
||||||
|
const p = new Point(x, y);
|
||||||
|
if (this.link.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
const bps = convertToBezierParams(this.link.datas.points);
|
||||||
|
for (let i = 0; i < bps.length; i++) {
|
||||||
|
const bp = bps[i];
|
||||||
|
if (
|
||||||
|
pointPolygon(
|
||||||
|
p,
|
||||||
|
[bp.p1, bp.cp1, bp.cp2, bp.p2],
|
||||||
|
this.link.datas.lineWidth
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
for (let i = 1; i < this.link.datas.points.length; i++) {
|
||||||
|
const p1 = this.link.datas.points[i - 1];
|
||||||
|
const p2 = this.link.datas.points[i];
|
||||||
|
if (linePoint(p1, p2, p, this.link.datas.lineWidth)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建吸附位置
|
||||||
|
* @param link
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function buildAbsorbablePositions(link: Link): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
const links = link.queryStore.queryByType<Link>(Link.Type);
|
||||||
|
links.forEach((other) => {
|
||||||
|
if (other.id == link.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const apa = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getStartPoint())
|
||||||
|
);
|
||||||
|
const apb = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getEndPoint())
|
||||||
|
);
|
||||||
|
aps.push(apa, apb);
|
||||||
|
});
|
||||||
|
|
||||||
|
aps.push(new AbsorbableCircle(new Point(450, 410), 30));
|
||||||
|
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 端点拖拽添加吸附位置
|
||||||
|
* @param g
|
||||||
|
* @param dp
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
function onEditPointCreate(
|
||||||
|
g: ILineGraphic,
|
||||||
|
dp: DraggablePoint,
|
||||||
|
index: number
|
||||||
|
): void {
|
||||||
|
const link = g as Link;
|
||||||
|
if (index === 0 || index == link.datas.points.length - 1) {
|
||||||
|
// 端点
|
||||||
|
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
link.getGraphicApp().setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(link),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* link路径编辑
|
||||||
|
*/
|
||||||
|
export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
|
||||||
|
static Name = 'LinkPointsDrag';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(LinkPointsEditPlugin.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp): LinkPointsEditPlugin {
|
||||||
|
return new LinkPointsEditPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Link[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type == Link.Type) as Link[];
|
||||||
|
}
|
||||||
|
bind(g: Link): void {
|
||||||
|
g.lineGraphic.eventMode = 'static';
|
||||||
|
g.lineGraphic.cursor = 'pointer';
|
||||||
|
g.lineGraphic.hitArea = new LinkGraphicHitArea(g);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
g.on('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
unbind(g: Link): void {
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
g.off('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject): void {
|
||||||
|
const link = g as Link;
|
||||||
|
let lep;
|
||||||
|
if (link.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
lep = link.getAssistantAppend<BezierCurveEditPlugin>(
|
||||||
|
BezierCurveEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (!lep) {
|
||||||
|
lep = new BezierCurveEditPlugin(link, {
|
||||||
|
onEditPointCreate,
|
||||||
|
});
|
||||||
|
link.addAssistantAppend(lep);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
lep = link.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (!lep) {
|
||||||
|
lep = new PolylineEditPlugin(link, { onEditPointCreate });
|
||||||
|
link.addAssistantAppend(lep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lep.showAll();
|
||||||
|
}
|
||||||
|
onUnselected(g: DisplayObject): void {
|
||||||
|
const link = g as Link;
|
||||||
|
let lep;
|
||||||
|
if (link.datas.curve) {
|
||||||
|
// 曲线
|
||||||
|
lep = link.getAssistantAppend<BezierCurveEditPlugin>(
|
||||||
|
BezierCurveEditPlugin.Name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 直线
|
||||||
|
lep = link.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (lep) {
|
||||||
|
lep.hideAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
159
src/graphics/pathLine/PathLine.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { JlGraphic, GraphicData, JlGraphicTemplate } from 'src/jl-graphic';
|
||||||
|
import { Graphics, IPointData, Point } from 'pixi.js';
|
||||||
|
import { RunLine } from '../runLine/RunLine';
|
||||||
|
import { getDrawApp } from 'src/drawApp';
|
||||||
|
// calculateDistanceFromPointToLine
|
||||||
|
import { calculateFootPointFromPointToLine, distance } from 'src/jl-graphic';
|
||||||
|
import { StationLine } from '../stationLine/StationLine';
|
||||||
|
export interface KilometerPoint {
|
||||||
|
get point(): IPointData;
|
||||||
|
set point(point: IPointData);
|
||||||
|
get kilometer(): number;
|
||||||
|
set kilometer(kilometer: number);
|
||||||
|
get stName(): string;
|
||||||
|
set stName(stName: string);
|
||||||
|
}
|
||||||
|
export interface IPathLineData extends GraphicData {
|
||||||
|
get code(): string;
|
||||||
|
set code(v: string);
|
||||||
|
get points(): IPointData[]; // 线坐标点
|
||||||
|
set points(points: IPointData[]);
|
||||||
|
get isUp(): boolean;
|
||||||
|
set isUp(v: boolean);
|
||||||
|
get kilometerPoints(): KilometerPoint[];
|
||||||
|
set kilometerPoints(kilometerPoints: KilometerPoint[]);
|
||||||
|
clone(): IPathLineData;
|
||||||
|
copyFrom(data: IPathLineData): void;
|
||||||
|
eq(other: IPathLineData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pathLineConsts = {
|
||||||
|
pathLineWidth: 1,
|
||||||
|
pathLineColor: '0Xff0000',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PathLine extends JlGraphic {
|
||||||
|
static Type = 'PathLine';
|
||||||
|
pathLine: Graphics = new Graphics();
|
||||||
|
constructor() {
|
||||||
|
super(PathLine.Type);
|
||||||
|
this.addChild(this.pathLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IPathLineData {
|
||||||
|
return this.getDatas<IPathLineData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
if (this.datas.points.length < 2) {
|
||||||
|
throw new Error('RunLine坐标数据异常');
|
||||||
|
}
|
||||||
|
this.pathLine.clear();
|
||||||
|
this.pathLine.lineStyle({
|
||||||
|
width: pathLineConsts.pathLineWidth,
|
||||||
|
color: pathLineConsts.pathLineColor,
|
||||||
|
});
|
||||||
|
const start = this.getStartPoint();
|
||||||
|
this.pathLine.moveTo(start.x, start.y);
|
||||||
|
for (let i = 0; i < this.datas.points.length; i++) {
|
||||||
|
const p = this.datas.points[i];
|
||||||
|
this.pathLine.lineTo(p.x, p.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get linePoints(): IPointData[] {
|
||||||
|
return this.datas.points;
|
||||||
|
}
|
||||||
|
set linePoints(points: IPointData[]) {
|
||||||
|
const old = this.datas.clone();
|
||||||
|
old.points = points;
|
||||||
|
this.updateData(old);
|
||||||
|
}
|
||||||
|
getStartPoint(): IPointData {
|
||||||
|
return this.datas.points[0];
|
||||||
|
}
|
||||||
|
getEndPoint(): IPointData {
|
||||||
|
return this.datas.points[this.datas.points.length - 1];
|
||||||
|
}
|
||||||
|
onDelete(): void {
|
||||||
|
super.onDelete();
|
||||||
|
const pathLineId = this.datas.id;
|
||||||
|
const app = getDrawApp();
|
||||||
|
if (!app) return;
|
||||||
|
const runLineList = app.queryStore.queryByType(RunLine.Type) as RunLine[];
|
||||||
|
runLineList.find((runLine) => {
|
||||||
|
if (runLine.datas.linkPathLines.includes(pathLineId)) {
|
||||||
|
const list = [...runLine.datas.linkPathLines];
|
||||||
|
const index = list.findIndex((item) => item === pathLineId);
|
||||||
|
list.splice(index, 1);
|
||||||
|
runLine.datas.linkPathLines = list;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 计算stationLine在轨迹线上的垂足距离,计算stationLine与轨迹点距离,寻求最短且垂足在线段上
|
||||||
|
* @param stas stationLine列表
|
||||||
|
*/
|
||||||
|
generatePathLineKilometerPoints(stas: string[]) {
|
||||||
|
const kilometerPoints: KilometerPoint[] = [];
|
||||||
|
stas.forEach((stasId) => {
|
||||||
|
const sta = this.queryStore.queryById(stasId) as StationLine;
|
||||||
|
let fp: IPointData | null = null;
|
||||||
|
const sp = sta.position;
|
||||||
|
let minDistance = Number.MAX_SAFE_INTEGER;
|
||||||
|
this.datas.points.forEach((p, index) => {
|
||||||
|
if (index) {
|
||||||
|
const prep = this.datas.points[index - 1];
|
||||||
|
const fpn = calculateFootPointFromPointToLine(prep, p, sp);
|
||||||
|
const distanceS = distance(prep.x, prep.y, p.x, p.y);
|
||||||
|
const fln = distance(fpn.x, fpn.y, sp.x, sp.y);
|
||||||
|
const distanceP = distance(p.x, p.y, sp.x, sp.y);
|
||||||
|
if (
|
||||||
|
distance(fpn.x, fpn.y, prep.x, prep.y) <= distanceS &&
|
||||||
|
distance(fpn.x, fpn.y, p.x, p.y) <= distanceS &&
|
||||||
|
fln <= minDistance
|
||||||
|
) {
|
||||||
|
minDistance = fln;
|
||||||
|
fp = fpn;
|
||||||
|
}
|
||||||
|
if (distanceP < minDistance) {
|
||||||
|
if (distanceP < minDistance) {
|
||||||
|
minDistance = distanceP;
|
||||||
|
fp = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const distanceP = distance(p.x, p.y, sp.x, sp.y);
|
||||||
|
if (distanceP < minDistance) {
|
||||||
|
minDistance = distanceP;
|
||||||
|
fp = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (sta.datas.code === '延平门') {
|
||||||
|
console.log(fp);
|
||||||
|
}
|
||||||
|
if (fp) {
|
||||||
|
kilometerPoints.push({
|
||||||
|
point: new Point(fp?.['x'], fp?.['y']),
|
||||||
|
kilometer: 0,
|
||||||
|
stName: sta.datas.code,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.datas.kilometerPoints = kilometerPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PathLineTemplate extends JlGraphicTemplate<PathLine> {
|
||||||
|
pathLineColor: string;
|
||||||
|
pathLineWidth: number;
|
||||||
|
constructor(dataTemplate: IPathLineData) {
|
||||||
|
super(PathLine.Type, { dataTemplate });
|
||||||
|
this.pathLineColor = pathLineConsts.pathLineColor;
|
||||||
|
this.pathLineWidth = pathLineConsts.pathLineWidth;
|
||||||
|
}
|
||||||
|
new(): PathLine {
|
||||||
|
return new PathLine();
|
||||||
|
}
|
||||||
|
}
|
219
src/graphics/pathLine/PathLineDrawAssistant.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
JlDrawApp,
|
||||||
|
linePoint,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicApp,
|
||||||
|
JlGraphic,
|
||||||
|
DraggablePoint,
|
||||||
|
GraphicTransformEvent,
|
||||||
|
calculateFootPointFromPointToLine,
|
||||||
|
calculateDistanceFromPointToLine,
|
||||||
|
distance,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import {
|
||||||
|
IPathLineData,
|
||||||
|
PathLineTemplate,
|
||||||
|
PathLine,
|
||||||
|
pathLineConsts,
|
||||||
|
} from './PathLine';
|
||||||
|
import {
|
||||||
|
Point,
|
||||||
|
Graphics,
|
||||||
|
LINE_JOIN,
|
||||||
|
IHitArea,
|
||||||
|
DisplayObject,
|
||||||
|
IPointData,
|
||||||
|
} from 'pixi.js';
|
||||||
|
export interface IPathLineDrawOptions {
|
||||||
|
newData: () => IPathLineData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PathLineDraw extends GraphicDrawAssistant<
|
||||||
|
PathLineTemplate,
|
||||||
|
IPathLineData
|
||||||
|
> {
|
||||||
|
points: Point[] = [];
|
||||||
|
code = '';
|
||||||
|
graphic: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, template: PathLineTemplate) {
|
||||||
|
super(app, template, 'sym_o_horizontal_rule', '不展示');
|
||||||
|
this.container.addChild(this.graphic);
|
||||||
|
PathLinePointsEditPlugin.init(app);
|
||||||
|
}
|
||||||
|
clearCache(): void {
|
||||||
|
this.points = [];
|
||||||
|
this.code = '';
|
||||||
|
this.graphic.clear();
|
||||||
|
}
|
||||||
|
// onRightClick(): void {
|
||||||
|
// this.createAndStore(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
// const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
// const p = new Point(x, y);
|
||||||
|
// this.points.push(p);
|
||||||
|
// }
|
||||||
|
quickCreate(points: Point[], lineId: string) {
|
||||||
|
this.points = [...points];
|
||||||
|
this.code = lineId;
|
||||||
|
return this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
if (this.points.length < 1) return;
|
||||||
|
this.graphic.clear();
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
this.graphic.lineStyle({
|
||||||
|
width: template.pathLineWidth,
|
||||||
|
color: template.pathLineColor,
|
||||||
|
join: LINE_JOIN.ROUND,
|
||||||
|
});
|
||||||
|
const ps = [...this.points];
|
||||||
|
ps.push(p);
|
||||||
|
// 直线
|
||||||
|
this.graphic.moveTo(ps[0].x, ps[0].y);
|
||||||
|
for (let i = 1; i < ps.length; i++) {
|
||||||
|
const p = ps[i];
|
||||||
|
this.graphic.lineTo(p.x, p.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prepareData(data: IPathLineData): boolean {
|
||||||
|
data.points = this.points;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PathLineGraphicHitArea implements IHitArea {
|
||||||
|
pathLine: PathLine;
|
||||||
|
constructor(pathLine: PathLine) {
|
||||||
|
this.pathLine = pathLine;
|
||||||
|
}
|
||||||
|
contains(x: number, y: number): boolean {
|
||||||
|
const p = new Point(x, y);
|
||||||
|
for (let i = 1; i < this.pathLine.datas.points.length; i++) {
|
||||||
|
const p1 = this.pathLine.datas.points[i - 1];
|
||||||
|
const p2 = this.pathLine.datas.points[i];
|
||||||
|
if (linePoint(p1, p2, p, pathLineConsts.pathLineWidth)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PathLinePointsEditPlugin extends GraphicInteractionPlugin<PathLine> {
|
||||||
|
static Name = 'LinkPointsDrag';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(PathLinePointsEditPlugin.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp): PathLinePointsEditPlugin {
|
||||||
|
return new PathLinePointsEditPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): PathLine[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type == PathLine.Type) as PathLine[];
|
||||||
|
}
|
||||||
|
bind(g: PathLine): void {
|
||||||
|
g.pathLine.eventMode = 'static';
|
||||||
|
g.pathLine.cursor = 'pointer';
|
||||||
|
g.pathLine.draggable = false;
|
||||||
|
g.pathLine.scalable = false;
|
||||||
|
g.pathLine.rotatable = false;
|
||||||
|
g.pathLine.hitArea = new PathLineGraphicHitArea(g);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
g.on('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
unbind(g: PathLine): void {
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
g.off('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject): void {
|
||||||
|
const pathLine = g as PathLine;
|
||||||
|
pathLine.draggable = false;
|
||||||
|
pathLine.scalable = false;
|
||||||
|
pathLine.rotatable = false;
|
||||||
|
pathLine.datas.kilometerPoints.forEach((kp, index) => {
|
||||||
|
const dp = new DraggablePoint(kp.point);
|
||||||
|
dp.on(
|
||||||
|
'transforming',
|
||||||
|
(e: GraphicTransformEvent) => {
|
||||||
|
this.movePointOnLine(pathLine.datas.points, e);
|
||||||
|
},
|
||||||
|
this
|
||||||
|
);
|
||||||
|
dp.on(
|
||||||
|
'transformend',
|
||||||
|
(e: GraphicTransformEvent) => {
|
||||||
|
const kilometerPoints = [...pathLine.datas.kilometerPoints];
|
||||||
|
kilometerPoints[index] = {
|
||||||
|
point: new Point(e.target.position.x, e.target.position.y),
|
||||||
|
kilometer: kilometerPoints[index].kilometer,
|
||||||
|
stName: kilometerPoints[index].stName,
|
||||||
|
};
|
||||||
|
pathLine.datas.kilometerPoints = kilometerPoints;
|
||||||
|
},
|
||||||
|
this
|
||||||
|
);
|
||||||
|
pathLine.addChild(dp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onUnselected(g: DisplayObject): void {
|
||||||
|
const pathLine = g as PathLine;
|
||||||
|
if (pathLine.children.length > 1) {
|
||||||
|
pathLine.removeChildren(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kpTransforming(e: GraphicTransformEvent) {
|
||||||
|
console.log(e, '----');
|
||||||
|
}
|
||||||
|
movePointOnLine(points: IPointData[], e: GraphicTransformEvent) {
|
||||||
|
let minDistance = Number.MAX_SAFE_INTEGER;
|
||||||
|
let fp = null;
|
||||||
|
const np = e.target.position;
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const distancep = distance(np.x, np.y, points[i].x, points[i].y);
|
||||||
|
if (i !== 0) {
|
||||||
|
const distanceF = calculateDistanceFromPointToLine(
|
||||||
|
points[i - 1],
|
||||||
|
points[i],
|
||||||
|
np
|
||||||
|
);
|
||||||
|
const fpn = calculateFootPointFromPointToLine(
|
||||||
|
points[i - 1],
|
||||||
|
points[i],
|
||||||
|
np
|
||||||
|
);
|
||||||
|
const distance1 = distance(
|
||||||
|
fpn.x,
|
||||||
|
fpn.y,
|
||||||
|
points[i - 1].x,
|
||||||
|
points[i - 1].y
|
||||||
|
);
|
||||||
|
const distance2 = distance(fpn.x, fpn.y, points[i].x, points[i].y);
|
||||||
|
const distance3 = distance(
|
||||||
|
points[i - 1].x,
|
||||||
|
points[i - 1].y,
|
||||||
|
points[i].x,
|
||||||
|
points[i].y
|
||||||
|
);
|
||||||
|
if (distancep < minDistance) {
|
||||||
|
minDistance = distancep;
|
||||||
|
fp = points[i];
|
||||||
|
}
|
||||||
|
if (distanceF < minDistance && distance1 + distance2 <= distance3 + 1) {
|
||||||
|
minDistance = distanceF;
|
||||||
|
fp = fpn;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
minDistance = distancep;
|
||||||
|
fp = points[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fp) {
|
||||||
|
e.target.position.set(fp.x, fp.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
429
src/graphics/platform/Platform.ts
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
import { Color, Container, Graphics, Rectangle } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
GraphicState,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
getRectangleCenter,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface IPlatformData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get hasdoor(): boolean; // 是否有屏蔽门
|
||||||
|
set hasdoor(v: boolean);
|
||||||
|
get direction(): string; // 屏蔽门上下
|
||||||
|
set direction(v: string);
|
||||||
|
clone(): IPlatformData;
|
||||||
|
copyFrom(data: IPlatformData): void;
|
||||||
|
eq(other: IPlatformData): boolean;
|
||||||
|
}
|
||||||
|
export interface IPlatformState extends GraphicState {
|
||||||
|
get emergstop(): boolean;
|
||||||
|
set emergstop(v: boolean);
|
||||||
|
get trainberth(): boolean;
|
||||||
|
set trainberth(v: boolean);
|
||||||
|
get close(): boolean;
|
||||||
|
set close(v: boolean);
|
||||||
|
get upHold(): boolean;
|
||||||
|
set upHold(v: boolean);
|
||||||
|
get downHold(): boolean;
|
||||||
|
set downHold(v: boolean);
|
||||||
|
get upOccHold(): boolean;
|
||||||
|
set upOccHold(v: boolean);
|
||||||
|
get downOccHold(): boolean;
|
||||||
|
set downOccHold(v: boolean);
|
||||||
|
get psdOpen(): boolean;
|
||||||
|
set psdOpen(v: boolean);
|
||||||
|
get psdCut(): boolean;
|
||||||
|
set psdCut(v: boolean);
|
||||||
|
get upSkipstop(): boolean;
|
||||||
|
set upSkipstop(v: boolean);
|
||||||
|
get downSkipstop(): boolean;
|
||||||
|
set downSkipstop(v: boolean);
|
||||||
|
get upTrainSkipstop(): boolean;
|
||||||
|
set upTrainSkipstop(v: boolean);
|
||||||
|
get downTrainSkipstop(): boolean;
|
||||||
|
set downTrainSkipstop(v: boolean);
|
||||||
|
get nextSectionRunTime(): number; //下一区间运行时间
|
||||||
|
set nextSectionRunTime(v: number);
|
||||||
|
get nextSectionRunLevel(): number; //下一区间运行等级
|
||||||
|
set nextSectionRunLevel(v: number);
|
||||||
|
get stopTime(): number; //停站时间
|
||||||
|
set stopTime(v: number);
|
||||||
|
}
|
||||||
|
|
||||||
|
//站台颜色
|
||||||
|
export enum PlatformColorEnum {
|
||||||
|
grey = '0x7F7F7F', //站台没有列车停站
|
||||||
|
yellow = '0xfbff00', //列车在站台停站
|
||||||
|
blue = '0xC0C0FE', //列车在站台跳停
|
||||||
|
lozengeRed = '0xff0000', //站台旁的菱形图标
|
||||||
|
whiteNumbers = '0xffffff', //站台旁白色数字
|
||||||
|
whiteCircle = '0xffffff', //H字符旁的圆圈
|
||||||
|
HCharYellow = '0xfbff00', //站台旁的H字符
|
||||||
|
HCharWhite = '0xffffff',
|
||||||
|
HCharRed = '0xff0000',
|
||||||
|
doorGreen = '0x00FF00', //屏蔽门的颜色
|
||||||
|
doorRed = '0xff0000',
|
||||||
|
doorBlue = '0x4048C4',
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformConsts = {
|
||||||
|
width: 90,
|
||||||
|
height: 20,
|
||||||
|
lineWidth: 3,
|
||||||
|
besideFontSize: 12,
|
||||||
|
doorOpenSpacing: 15,
|
||||||
|
doorPlatformSpacing: 10,
|
||||||
|
besideSpacing: 10,
|
||||||
|
circleRadius: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
//子元素--矩形
|
||||||
|
export class rectGraphic extends Container {
|
||||||
|
static Type = 'RectPlatForm';
|
||||||
|
rectGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.rectGraphic = new Graphics();
|
||||||
|
this.addChild(this.rectGraphic);
|
||||||
|
}
|
||||||
|
draw(state: IPlatformState): void {
|
||||||
|
const rectGraphic = this.rectGraphic;
|
||||||
|
rectGraphic.clear();
|
||||||
|
let fillColor = PlatformColorEnum.grey;
|
||||||
|
if (state.trainberth) {
|
||||||
|
fillColor = PlatformColorEnum.yellow;
|
||||||
|
}
|
||||||
|
if (state.upSkipstop || state.downSkipstop) {
|
||||||
|
fillColor = PlatformColorEnum.blue;
|
||||||
|
}
|
||||||
|
rectGraphic.lineStyle(platformConsts.lineWidth, new Color(fillColor));
|
||||||
|
rectGraphic.beginFill(fillColor, 1);
|
||||||
|
rectGraphic.drawRect(0, 0, platformConsts.width, platformConsts.height);
|
||||||
|
rectGraphic.endFill;
|
||||||
|
const rectP = new Rectangle(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
platformConsts.width,
|
||||||
|
platformConsts.height
|
||||||
|
);
|
||||||
|
rectGraphic.pivot = getRectangleCenter(rectP);
|
||||||
|
}
|
||||||
|
clear(): void {
|
||||||
|
this.rectGraphic.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//子元素--门
|
||||||
|
export class doorGraphic extends Container {
|
||||||
|
static Type = 'Door';
|
||||||
|
doorGraphic: Graphics;
|
||||||
|
doorCloseGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.doorGraphic = new Graphics();
|
||||||
|
this.doorCloseGraphic = new Graphics();
|
||||||
|
this.addChild(this.doorGraphic);
|
||||||
|
this.addChild(this.doorCloseGraphic);
|
||||||
|
}
|
||||||
|
draw(stateData: IPlatformState): void {
|
||||||
|
const doorGraphic = this.doorGraphic;
|
||||||
|
const doorCloseGraphic = this.doorCloseGraphic;
|
||||||
|
doorGraphic.clear();
|
||||||
|
doorCloseGraphic.clear();
|
||||||
|
let lineColor = PlatformColorEnum.doorGreen;
|
||||||
|
if (stateData.psdCut) {
|
||||||
|
lineColor = PlatformColorEnum.doorRed;
|
||||||
|
}
|
||||||
|
doorGraphic.lineStyle(platformConsts.lineWidth, new Color(lineColor));
|
||||||
|
doorGraphic.moveTo(
|
||||||
|
-platformConsts.width / 2 - platformConsts.lineWidth / 2,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
doorGraphic.lineTo(-platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorGraphic.moveTo(platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorGraphic.lineTo(
|
||||||
|
platformConsts.width / 2 + platformConsts.lineWidth / 2,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
//屏蔽门闭合
|
||||||
|
doorCloseGraphic.lineStyle(platformConsts.lineWidth, new Color(lineColor));
|
||||||
|
doorCloseGraphic.moveTo(-platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorCloseGraphic.lineTo(platformConsts.doorOpenSpacing, 0);
|
||||||
|
doorGraphic.position.set(
|
||||||
|
0,
|
||||||
|
-platformConsts.height / 2 - platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
doorCloseGraphic.position.set(
|
||||||
|
0,
|
||||||
|
-platformConsts.height / 2 - platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
}
|
||||||
|
clear(): void {
|
||||||
|
this.doorGraphic.clear();
|
||||||
|
this.doorCloseGraphic.clear();
|
||||||
|
}
|
||||||
|
changeState(stateData: IPlatformState): void {
|
||||||
|
if (stateData.psdOpen) {
|
||||||
|
this.doorCloseGraphic.visible = false;
|
||||||
|
} else {
|
||||||
|
this.doorCloseGraphic.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//子元素--字符
|
||||||
|
class codeGraph extends Container {
|
||||||
|
static Type = 'Code';
|
||||||
|
character: VectorText = new VectorText(''); //扣车H
|
||||||
|
runTime: VectorText = new VectorText(''); //运行时间
|
||||||
|
stopTime: VectorText = new VectorText(''); //停站时间
|
||||||
|
circle: Graphics = new Graphics();
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.addChild(this.character);
|
||||||
|
this.addChild(this.runTime);
|
||||||
|
this.addChild(this.circle);
|
||||||
|
this.addChild(this.stopTime);
|
||||||
|
this.character.setVectorFontSize(platformConsts.besideFontSize);
|
||||||
|
this.runTime.setVectorFontSize(platformConsts.besideFontSize);
|
||||||
|
this.stopTime.setVectorFontSize(platformConsts.besideFontSize);
|
||||||
|
}
|
||||||
|
draw(): void {
|
||||||
|
//扣车
|
||||||
|
const character = this.character;
|
||||||
|
character.text = 'H';
|
||||||
|
character.anchor.set(0.5);
|
||||||
|
character.position.set(
|
||||||
|
-platformConsts.width / 2 -
|
||||||
|
platformConsts.lineWidth / 2 -
|
||||||
|
(platformConsts.besideSpacing * 2) / 3,
|
||||||
|
(platformConsts.height * 3) / 4
|
||||||
|
);
|
||||||
|
character.style.fill = PlatformColorEnum.whiteNumbers;
|
||||||
|
const circle = this.circle;
|
||||||
|
circle.clear();
|
||||||
|
circle.lineStyle(0.5, PlatformColorEnum.whiteCircle);
|
||||||
|
circle.drawCircle(0, 0, platformConsts.circleRadius);
|
||||||
|
circle.position.set(
|
||||||
|
-platformConsts.width / 2 -
|
||||||
|
platformConsts.lineWidth / 2 -
|
||||||
|
(platformConsts.besideSpacing * 4) / 3,
|
||||||
|
(platformConsts.height * 3) / 5
|
||||||
|
);
|
||||||
|
//区间运行等级状态
|
||||||
|
const runTime = this.runTime;
|
||||||
|
runTime.anchor.set(0.5);
|
||||||
|
runTime.position.set(
|
||||||
|
platformConsts.width / 2 +
|
||||||
|
platformConsts.lineWidth / 2 +
|
||||||
|
platformConsts.besideSpacing,
|
||||||
|
-platformConsts.besideSpacing
|
||||||
|
);
|
||||||
|
runTime.style.fill = PlatformColorEnum.whiteNumbers;
|
||||||
|
//停站时间
|
||||||
|
const stopTime = this.stopTime;
|
||||||
|
stopTime.anchor.set(0.5);
|
||||||
|
stopTime.position.set(
|
||||||
|
platformConsts.width / 2 +
|
||||||
|
platformConsts.lineWidth / 2 +
|
||||||
|
platformConsts.besideSpacing,
|
||||||
|
platformConsts.besideSpacing
|
||||||
|
);
|
||||||
|
stopTime.style.fill = PlatformColorEnum.whiteNumbers;
|
||||||
|
character.visible = false;
|
||||||
|
circle.visible = false;
|
||||||
|
runTime.visible = false;
|
||||||
|
stopTime.visible = false;
|
||||||
|
}
|
||||||
|
clear(): void {
|
||||||
|
this.character.destroy();
|
||||||
|
}
|
||||||
|
changeState(stateData: IPlatformState): void {
|
||||||
|
if (
|
||||||
|
stateData.upHold ||
|
||||||
|
stateData.upOccHold ||
|
||||||
|
stateData.downHold ||
|
||||||
|
stateData.downOccHold
|
||||||
|
) {
|
||||||
|
this.character.text = 'H';
|
||||||
|
this.character.visible = true;
|
||||||
|
this.circle.visible = true;
|
||||||
|
//上行扣车
|
||||||
|
if (stateData.upHold) {
|
||||||
|
this.character.style.fill = PlatformColorEnum.HCharYellow;
|
||||||
|
}
|
||||||
|
if (stateData.upOccHold) {
|
||||||
|
this.character.style.fill = PlatformColorEnum.HCharWhite;
|
||||||
|
}
|
||||||
|
if (stateData.upHold && stateData.upOccHold) {
|
||||||
|
this.character.style.fill = PlatformColorEnum.HCharRed;
|
||||||
|
}
|
||||||
|
//下行扣车
|
||||||
|
if (stateData.downHold) {
|
||||||
|
this.character.style.fill = PlatformColorEnum.HCharYellow;
|
||||||
|
}
|
||||||
|
if (stateData.downOccHold) {
|
||||||
|
this.character.style.fill = PlatformColorEnum.HCharWhite;
|
||||||
|
}
|
||||||
|
if (stateData.downHold && stateData.downOccHold) {
|
||||||
|
this.character.style.fill = PlatformColorEnum.HCharRed;
|
||||||
|
}
|
||||||
|
//运行等级
|
||||||
|
if (stateData.nextSectionRunTime) {
|
||||||
|
this.runTime.visible = true;
|
||||||
|
this.runTime.text = stateData.nextSectionRunTime;
|
||||||
|
}
|
||||||
|
//停站时间
|
||||||
|
if (stateData.stopTime) {
|
||||||
|
this.stopTime.visible = true;
|
||||||
|
this.stopTime.text = stateData.stopTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//子元素--站台旁菱形图标
|
||||||
|
class besideGraphic extends Container {
|
||||||
|
static Type = 'BesideGraphic';
|
||||||
|
besideGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.besideGraphic = new Graphics();
|
||||||
|
this.addChild(this.besideGraphic);
|
||||||
|
}
|
||||||
|
draw(): void {
|
||||||
|
const besideGraphic = this.besideGraphic;
|
||||||
|
besideGraphic.clear();
|
||||||
|
besideGraphic.lineStyle(1, new Color(PlatformColorEnum.lozengeRed));
|
||||||
|
besideGraphic.beginFill(PlatformColorEnum.lozengeRed, 1);
|
||||||
|
besideGraphic.drawRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
platformConsts.height / 4,
|
||||||
|
platformConsts.height / 4
|
||||||
|
);
|
||||||
|
besideGraphic.endFill();
|
||||||
|
const rect = new Rectangle(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
platformConsts.height / 4,
|
||||||
|
platformConsts.height / 4
|
||||||
|
);
|
||||||
|
besideGraphic.pivot = getRectangleCenter(rect);
|
||||||
|
besideGraphic.rotation = Math.PI / 4;
|
||||||
|
besideGraphic.position.set(
|
||||||
|
0,
|
||||||
|
-platformConsts.height / 2 -
|
||||||
|
platformConsts.doorPlatformSpacing -
|
||||||
|
platformConsts.height / 3
|
||||||
|
);
|
||||||
|
besideGraphic.visible = false;
|
||||||
|
}
|
||||||
|
clear(): void {
|
||||||
|
this.besideGraphic.clear();
|
||||||
|
}
|
||||||
|
changeState(stateData: IPlatformState): void {
|
||||||
|
if (stateData.emergstop) {
|
||||||
|
this.besideGraphic.visible = true;
|
||||||
|
} else {
|
||||||
|
this.besideGraphic.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Platform extends JlGraphic {
|
||||||
|
static Type = 'Platform';
|
||||||
|
platformGraphic: rectGraphic = new rectGraphic();
|
||||||
|
doorGraphic: doorGraphic = new doorGraphic();
|
||||||
|
besideGraphic: besideGraphic = new besideGraphic();
|
||||||
|
codeGraph: codeGraph = new codeGraph();
|
||||||
|
constructor() {
|
||||||
|
super(Platform.Type);
|
||||||
|
this.addChild(this.platformGraphic);
|
||||||
|
this.addChild(this.doorGraphic);
|
||||||
|
this.addChild(this.besideGraphic);
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IPlatformData {
|
||||||
|
return this.getDatas<IPlatformData>();
|
||||||
|
}
|
||||||
|
get states(): IPlatformState {
|
||||||
|
return this.getStates<IPlatformState>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
this.doorGraphic.clear();
|
||||||
|
if (this.datas.hasdoor) {
|
||||||
|
this.doorGraphic.draw(this.states);
|
||||||
|
}
|
||||||
|
this.platformGraphic.draw(this.states);
|
||||||
|
this.besideGraphic.draw();
|
||||||
|
this.codeGraph.draw();
|
||||||
|
this.doorGraphic.position.set(0, 0);
|
||||||
|
this.besideGraphic.position.set(0, 0);
|
||||||
|
this.codeGraph.position.set(0, 0);
|
||||||
|
//站台方向
|
||||||
|
if (this.datas.direction == 'down') {
|
||||||
|
this.doorGraphic.position.set(
|
||||||
|
0,
|
||||||
|
platformConsts.height + platformConsts.doorPlatformSpacing * 2
|
||||||
|
);
|
||||||
|
this.besideGraphic.position.set(
|
||||||
|
0,
|
||||||
|
platformConsts.height +
|
||||||
|
platformConsts.doorPlatformSpacing * 2 +
|
||||||
|
(platformConsts.height * 2) / 3
|
||||||
|
);
|
||||||
|
this.codeGraph.children[0].position.set(
|
||||||
|
platformConsts.width / 2 +
|
||||||
|
platformConsts.lineWidth / 2 +
|
||||||
|
(platformConsts.besideSpacing * 2) / 3,
|
||||||
|
-(platformConsts.height * 3) / 4
|
||||||
|
);
|
||||||
|
this.codeGraph.children[1].position.set(
|
||||||
|
-platformConsts.width / 2 -
|
||||||
|
platformConsts.lineWidth / 2 -
|
||||||
|
platformConsts.besideSpacing,
|
||||||
|
platformConsts.besideSpacing
|
||||||
|
);
|
||||||
|
this.codeGraph.children[2].position.set(
|
||||||
|
platformConsts.width / 2 +
|
||||||
|
platformConsts.lineWidth / 2 +
|
||||||
|
(platformConsts.besideSpacing * 4) / 3,
|
||||||
|
(-platformConsts.height * 10) / 11
|
||||||
|
);
|
||||||
|
this.codeGraph.children[3].position.set(
|
||||||
|
-platformConsts.width / 2 -
|
||||||
|
platformConsts.lineWidth / 2 -
|
||||||
|
platformConsts.besideSpacing,
|
||||||
|
-platformConsts.besideSpacing
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.changeState();
|
||||||
|
}
|
||||||
|
changeState(): void {
|
||||||
|
this.doorGraphic.changeState(this.states);
|
||||||
|
this.besideGraphic.changeState(this.states);
|
||||||
|
this.codeGraph.changeState(this.states);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlatformTemplate extends JlGraphicTemplate<Platform> {
|
||||||
|
hasdoor: boolean;
|
||||||
|
direction: string;
|
||||||
|
constructor(dataTemplate: IPlatformData, stateTemplate: IPlatformState) {
|
||||||
|
super(Platform.Type, {
|
||||||
|
dataTemplate,
|
||||||
|
stateTemplate,
|
||||||
|
});
|
||||||
|
this.hasdoor = true;
|
||||||
|
this.direction = 'up';
|
||||||
|
}
|
||||||
|
new(): Platform {
|
||||||
|
const platform = new Platform();
|
||||||
|
platform.loadData(this.datas);
|
||||||
|
platform.loadState(this.states);
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
}
|
119
src/graphics/platform/PlatformDrawAssistant.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { FederatedPointerEvent, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
AbsorbableLine,
|
||||||
|
AbsorbablePosition,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IPlatformData,
|
||||||
|
Platform,
|
||||||
|
PlatformTemplate,
|
||||||
|
rectGraphic,
|
||||||
|
doorGraphic,
|
||||||
|
IPlatformState,
|
||||||
|
} from './Platform';
|
||||||
|
|
||||||
|
export interface IPlatformDrawOptions {
|
||||||
|
newData: () => IPlatformData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlatformDraw extends GraphicDrawAssistant<
|
||||||
|
PlatformTemplate,
|
||||||
|
IPlatformData
|
||||||
|
> {
|
||||||
|
platformGraphic: rectGraphic = new rectGraphic();
|
||||||
|
doorGraphic: doorGraphic = new doorGraphic();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, template: PlatformTemplate) {
|
||||||
|
super(
|
||||||
|
app,
|
||||||
|
template,
|
||||||
|
'svguse:../../drawIcon.svg#icon-platform',
|
||||||
|
'站台Platform'
|
||||||
|
);
|
||||||
|
this.container.addChild(this.platformGraphic);
|
||||||
|
this.container.addChild(this.doorGraphic);
|
||||||
|
platformInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
this.platformGraphic.draw(this.graphicTemplate.states as IPlatformState);
|
||||||
|
this.doorGraphic.draw(this.graphicTemplate.states as IPlatformState);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.platformGraphic.clear();
|
||||||
|
this.doorGraphic.clear();
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
this.container.position.copyFrom(this.toCanvasCoordinates(e.global));
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
this.container.position.copyFrom(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareData(data: IPlatformData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.hasdoor = template.hasdoor;
|
||||||
|
data.direction = template.direction;
|
||||||
|
data.transform = this.container.saveTransform();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAbsorbablePositions(platform: Platform): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
const platforms = platform.queryStore.queryByType<Platform>(Platform.Type);
|
||||||
|
const { width, height } = platform.getGraphicApp().canvas;
|
||||||
|
platforms.forEach((other) => {
|
||||||
|
if (other.id == platform.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ps = other.datas.transform.position;
|
||||||
|
const xs = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y });
|
||||||
|
const ys = new AbsorbableLine({ x: ps.x, y: 0 }, { x: ps.x, y: height });
|
||||||
|
aps.push(xs, ys);
|
||||||
|
});
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class platformInteraction extends GraphicInteractionPlugin<Platform> {
|
||||||
|
static Name = 'platform_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(platformInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new platformInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Platform[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Platform.Type)
|
||||||
|
.map((g) => g as Platform);
|
||||||
|
}
|
||||||
|
bind(g: Platform): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
unbind(g: Platform): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
}
|
||||||
|
onSelected(): void {
|
||||||
|
const platform = this.app.selectedGraphics[0] as Platform;
|
||||||
|
this.app.setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(platform),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
72
src/graphics/polygon/Polygon.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { Color, Graphics, IPointData } from 'pixi.js';
|
||||||
|
import { GraphicData, JlGraphic, JlGraphicTemplate } from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface IPolygonData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get lineWidth(): number; // 线宽
|
||||||
|
set lineWidth(v: number);
|
||||||
|
get lineColor(): string; // 线色
|
||||||
|
set lineColor(v: string);
|
||||||
|
get points(): IPointData[]; // 多边形坐标点
|
||||||
|
set points(points: IPointData[]);
|
||||||
|
clone(): IPolygonData;
|
||||||
|
copyFrom(data: IPolygonData): void;
|
||||||
|
eq(other: IPolygonData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const polygonConsts = {
|
||||||
|
lineWidth: 2,
|
||||||
|
lineColor: '0xff0000',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Polygon extends JlGraphic {
|
||||||
|
static Type = 'Polygon';
|
||||||
|
polygonGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super(Polygon.Type);
|
||||||
|
this.polygonGraphic = new Graphics();
|
||||||
|
this.addChild(this.polygonGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IPolygonData {
|
||||||
|
return this.getDatas<IPolygonData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
const polygonGraphic = this.polygonGraphic;
|
||||||
|
polygonGraphic.clear();
|
||||||
|
polygonGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColor)
|
||||||
|
);
|
||||||
|
polygonGraphic.drawPolygon(this.datas.points);
|
||||||
|
}
|
||||||
|
get linePoints(): IPointData[] {
|
||||||
|
return this.datas.points;
|
||||||
|
}
|
||||||
|
set linePoints(points: IPointData[]) {
|
||||||
|
const old = this.datas.clone();
|
||||||
|
old.points = points;
|
||||||
|
this.updateData(old);
|
||||||
|
}
|
||||||
|
addOnePoints(): IPointData[] {
|
||||||
|
const ps = [...this.datas.points];
|
||||||
|
ps.push(this.datas.points[0]);
|
||||||
|
return ps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PolygonTemplate extends JlGraphicTemplate<Polygon> {
|
||||||
|
lineWidth: number;
|
||||||
|
lineColor: string;
|
||||||
|
constructor(dataTemplate: IPolygonData) {
|
||||||
|
super(Polygon.Type, {
|
||||||
|
dataTemplate,
|
||||||
|
});
|
||||||
|
this.lineWidth = polygonConsts.lineWidth;
|
||||||
|
this.lineColor = polygonConsts.lineColor;
|
||||||
|
}
|
||||||
|
new(): Polygon {
|
||||||
|
return new Polygon();
|
||||||
|
}
|
||||||
|
}
|
195
src/graphics/polygon/PolygonDrawAssistant.ts
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import {
|
||||||
|
FederatedPointerEvent,
|
||||||
|
Graphics,
|
||||||
|
Point,
|
||||||
|
IHitArea,
|
||||||
|
DisplayObject,
|
||||||
|
} from 'pixi.js';
|
||||||
|
import {
|
||||||
|
DraggablePoint,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicTransformEvent,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
linePoint,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
import AbsorbablePoint, {
|
||||||
|
AbsorbablePosition,
|
||||||
|
} from 'src/jl-graphic/graphic/AbsorbablePosition';
|
||||||
|
import {
|
||||||
|
ILineGraphic,
|
||||||
|
PolylineEditPlugin,
|
||||||
|
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
|
||||||
|
|
||||||
|
import { IPolygonData, Polygon, PolygonTemplate } from './Polygon';
|
||||||
|
|
||||||
|
export interface IPolygonDrawOptions {
|
||||||
|
newData: () => IPolygonData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PolygonDraw extends GraphicDrawAssistant<
|
||||||
|
PolygonTemplate,
|
||||||
|
IPolygonData
|
||||||
|
> {
|
||||||
|
points: Point[] = [];
|
||||||
|
polygonGraphic: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, template: PolygonTemplate) {
|
||||||
|
super(app, template, 'sym_o_square', '多边形Polygon');
|
||||||
|
this.container.addChild(this.polygonGraphic);
|
||||||
|
PolygonPointsEditPlugin.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.points = [];
|
||||||
|
this.polygonGraphic.clear();
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
this.points.push(p);
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
redraw(p: Point): void {
|
||||||
|
if (this.points.length < 1) return;
|
||||||
|
const polygonGraphic = this.polygonGraphic;
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
const ps = [...this.points];
|
||||||
|
ps.push(p);
|
||||||
|
polygonGraphic.clear();
|
||||||
|
polygonGraphic.lineStyle(template.lineWidth, template.lineColor);
|
||||||
|
polygonGraphic.drawPolygon(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareData(data: IPolygonData): boolean {
|
||||||
|
if (this.points.length < 2) {
|
||||||
|
console.log('Polygon绘制因点不够取消绘制');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.lineWidth = template.lineWidth;
|
||||||
|
data.lineColor = template.lineColor;
|
||||||
|
data.points = this.points;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//碰撞检测
|
||||||
|
export class PolygonGraphicHitArea implements IHitArea {
|
||||||
|
polygon: Polygon;
|
||||||
|
constructor(polygon: Polygon) {
|
||||||
|
this.polygon = polygon;
|
||||||
|
}
|
||||||
|
contains(x: number, y: number): boolean {
|
||||||
|
let contains = false;
|
||||||
|
const p = new Point(x, y);
|
||||||
|
const polygonData = this.polygon.datas;
|
||||||
|
//contains = pointPolygon(p, polygonData.points, polygonData.lineWidth);是否包含多边形内部
|
||||||
|
const ps = this.polygon.addOnePoints();
|
||||||
|
const tolerance = polygonData.lineWidth;
|
||||||
|
for (let i = 0; i < ps.length - 1; i++) {
|
||||||
|
const p1 = ps[i];
|
||||||
|
const p2 = ps[i + 1];
|
||||||
|
contains = contains || linePoint(p1, p2, p, tolerance);
|
||||||
|
if (contains) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contains;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建吸附位置
|
||||||
|
* @param polygon
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function buildAbsorbablePositions(polygon: Polygon): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
const polygons = polygon.queryStore.queryByType<Polygon>(Polygon.Type);
|
||||||
|
|
||||||
|
polygons.forEach((other) => {
|
||||||
|
if (other.id == polygon.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
other.linePoints.forEach((point) => {
|
||||||
|
const absorbablePoint = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(point)
|
||||||
|
);
|
||||||
|
aps.push(absorbablePoint);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 端点拖拽添加吸附位置
|
||||||
|
* @param g
|
||||||
|
* @param dp
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
function onEditPointCreate(g: ILineGraphic, dp: DraggablePoint): void {
|
||||||
|
const polygon = g as Polygon;
|
||||||
|
// 端点
|
||||||
|
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
polygon.getGraphicApp().setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(polygon),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* polygon路径编辑
|
||||||
|
*/
|
||||||
|
export class PolygonPointsEditPlugin extends GraphicInteractionPlugin<Polygon> {
|
||||||
|
static Name = 'PolygonPointsDrag';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(PolygonPointsEditPlugin.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp): PolygonPointsEditPlugin {
|
||||||
|
return new PolygonPointsEditPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Polygon[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type == Polygon.Type) as Polygon[];
|
||||||
|
}
|
||||||
|
bind(g: Polygon): void {
|
||||||
|
g.polygonGraphic.eventMode = 'static';
|
||||||
|
g.polygonGraphic.cursor = 'pointer';
|
||||||
|
g.polygonGraphic.hitArea = new PolygonGraphicHitArea(g);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
g.on('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
unbind(g: Polygon): void {
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
g.off('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject): void {
|
||||||
|
const polygon = g as Polygon;
|
||||||
|
let lep = polygon.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (!lep) {
|
||||||
|
lep = new PolylineEditPlugin(polygon, { onEditPointCreate });
|
||||||
|
polygon.addAssistantAppend(lep);
|
||||||
|
}
|
||||||
|
lep.showAll();
|
||||||
|
}
|
||||||
|
onUnselected(g: DisplayObject): void {
|
||||||
|
const polygon = g as Polygon;
|
||||||
|
const lep = polygon.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (lep) {
|
||||||
|
lep.hideAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
src/graphics/polygon/PolygonUtils.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { IPointData, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
calculateDistanceFromPointToLine,
|
||||||
|
calculateFootPointFromPointToLine,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
import { Polygon } from './Polygon';
|
||||||
|
|
||||||
|
//计算线段细分坐标--线段分成几份
|
||||||
|
export function getLineSegmentPoints(
|
||||||
|
startPoint: IPointData,
|
||||||
|
endPoint: IPointData,
|
||||||
|
knife: number
|
||||||
|
) {
|
||||||
|
const segmentLength = Math.sqrt(
|
||||||
|
Math.pow(endPoint.x - startPoint.x, 2) +
|
||||||
|
Math.pow(endPoint.y - startPoint.y, 2)
|
||||||
|
);
|
||||||
|
const segmentIncrement = segmentLength / knife;
|
||||||
|
const segmentAngle = Math.atan2(
|
||||||
|
endPoint.y - startPoint.y,
|
||||||
|
endPoint.x - startPoint.x
|
||||||
|
);
|
||||||
|
|
||||||
|
const points: IPointData[] = [];
|
||||||
|
for (let i = 1; i < knife; i++) {
|
||||||
|
const segmentPosition = i * segmentIncrement;
|
||||||
|
const x = startPoint.x + segmentPosition * Math.cos(segmentAngle);
|
||||||
|
const y = startPoint.y + segmentPosition * Math.sin(segmentAngle);
|
||||||
|
points.push(new Point(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取所选线段的索引
|
||||||
|
export function getWayLineIndex(
|
||||||
|
points: IPointData[],
|
||||||
|
p: IPointData
|
||||||
|
): { start: number; end: number } {
|
||||||
|
let start = 0;
|
||||||
|
let end = 0;
|
||||||
|
let minDistance = 0;
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const sp = points[i - 1];
|
||||||
|
const ep = points[i];
|
||||||
|
let distance = calculateDistanceFromPointToLine(sp, ep, p);
|
||||||
|
distance = Math.round(distance * 100) / 100;
|
||||||
|
if (i == 1) {
|
||||||
|
minDistance = distance;
|
||||||
|
}
|
||||||
|
if (distance == minDistance) {
|
||||||
|
const minX = Math.min(sp.x, ep.x);
|
||||||
|
const maxX = Math.max(sp.x, ep.x);
|
||||||
|
const minY = Math.min(sp.y, ep.y);
|
||||||
|
const maxY = Math.max(sp.y, ep.y);
|
||||||
|
const point = calculateFootPointFromPointToLine(sp, ep, p);
|
||||||
|
if (
|
||||||
|
point.x >= minX &&
|
||||||
|
point.x <= maxX &&
|
||||||
|
point.y >= minY &&
|
||||||
|
point.y <= maxY
|
||||||
|
) {
|
||||||
|
start = i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (distance < minDistance) {
|
||||||
|
minDistance = distance;
|
||||||
|
start = i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end = start + 1;
|
||||||
|
return { start, end };
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加细分的点的坐标
|
||||||
|
export function addPolygonSegmentingPoint(
|
||||||
|
graphic: Polygon,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
knife = 2
|
||||||
|
) {
|
||||||
|
const linePoints = graphic.addOnePoints();
|
||||||
|
const points = linePoints.slice(0, start + 1);
|
||||||
|
points.push(
|
||||||
|
...getLineSegmentPoints(linePoints[start], linePoints[end], knife)
|
||||||
|
);
|
||||||
|
points.push(...linePoints.slice(end));
|
||||||
|
points.pop();
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
99
src/graphics/rect/Rect.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { Color, Graphics, IPointData, Point, Rectangle } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
getRectangleCenter,
|
||||||
|
} from 'src/jl-graphic';
|
||||||
|
|
||||||
|
export interface IRectData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get lineWidth(): number; // 线宽
|
||||||
|
set lineWidth(v: number);
|
||||||
|
get lineColor(): string; // 线色
|
||||||
|
set lineColor(v: string);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
get width(): number; // 宽度
|
||||||
|
set width(v: number);
|
||||||
|
get height(): number; // 高度
|
||||||
|
set height(v: number);
|
||||||
|
get radius(): number; // 圆角半径
|
||||||
|
set radius(v: number);
|
||||||
|
clone(): IRectData;
|
||||||
|
copyFrom(data: IRectData): void;
|
||||||
|
eq(other: IRectData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rectConsts = {
|
||||||
|
lineWidth: 2,
|
||||||
|
lineColor: '0xff0000',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Rect extends JlGraphic {
|
||||||
|
static Type = 'Rect';
|
||||||
|
rectGraphic: Graphics = new Graphics();
|
||||||
|
constructor() {
|
||||||
|
super(Rect.Type);
|
||||||
|
this.addChild(this.rectGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IRectData {
|
||||||
|
return this.getDatas<IRectData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
const rectGraphic = this.rectGraphic;
|
||||||
|
rectGraphic.clear();
|
||||||
|
rectGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColor)
|
||||||
|
);
|
||||||
|
const radius = this.datas?.radius || 0;
|
||||||
|
rectGraphic.drawRoundedRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.datas.width,
|
||||||
|
this.datas.height,
|
||||||
|
radius
|
||||||
|
);
|
||||||
|
rectGraphic.pivot = getRectangleCenter(
|
||||||
|
new Rectangle(0, 0, this.datas.width, this.datas.height)
|
||||||
|
);
|
||||||
|
const transformPos = this.datas.transform.position;
|
||||||
|
if (transformPos.x == 0 && transformPos.y == 0) {
|
||||||
|
this.position.set(
|
||||||
|
this.datas.point.x + this.datas.width / 2,
|
||||||
|
this.datas.point.y + this.datas.height / 2
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.position.set(
|
||||||
|
this.datas.transform.position.x,
|
||||||
|
this.datas.transform.position.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rectPoints(): IPointData[] {
|
||||||
|
const r1 = new Point(this.datas.point.x, this.datas.point.y);
|
||||||
|
const r2 = new Point(r1.x + this.datas.width, r1.y);
|
||||||
|
const r3 = new Point(r1.x + this.datas.width, r1.y + this.datas.height);
|
||||||
|
const r4 = new Point(r1.x, r1.y + this.datas.height);
|
||||||
|
const rectPoints = [r1, r2, r3, r4, r1];
|
||||||
|
return rectPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RectTemplate extends JlGraphicTemplate<Rect> {
|
||||||
|
lineWidth: number;
|
||||||
|
lineColor: string;
|
||||||
|
constructor(dataTemplate: IRectData) {
|
||||||
|
super(Rect.Type, {
|
||||||
|
dataTemplate,
|
||||||
|
});
|
||||||
|
this.lineWidth = rectConsts.lineWidth;
|
||||||
|
this.lineColor = rectConsts.lineColor;
|
||||||
|
}
|
||||||
|
new(): Rect {
|
||||||
|
return new Rect();
|
||||||
|
}
|
||||||
|
}
|