Merge branch 'master' of http://khuhub.khu.ac.kr/2021-1-capstone-design1/TWT_Project1
Showing
173 changed files
with
4797 additions
and
0 deletions
Backend/.eslintrc.js
0 → 100644
1 | +module.exports = { | ||
2 | + parser: '@typescript-eslint/parser', | ||
3 | + parserOptions: { | ||
4 | + project: 'tsconfig.json', | ||
5 | + sourceType: 'module', | ||
6 | + }, | ||
7 | + plugins: ['@typescript-eslint/eslint-plugin'], | ||
8 | + extends: [ | ||
9 | + 'plugin:@typescript-eslint/recommended', | ||
10 | + 'plugin:prettier/recommended', | ||
11 | + ], | ||
12 | + root: true, | ||
13 | + env: { | ||
14 | + node: true, | ||
15 | + jest: true, | ||
16 | + }, | ||
17 | + ignorePatterns: ['.eslintrc.js'], | ||
18 | + rules: { | ||
19 | + '@typescript-eslint/interface-name-prefix': 'off', | ||
20 | + '@typescript-eslint/explicit-function-return-type': 'off', | ||
21 | + '@typescript-eslint/explicit-module-boundary-types': 'off', | ||
22 | + '@typescript-eslint/no-explicit-any': 'off', | ||
23 | + }, | ||
24 | +}; |
Backend/.gitignore
0 → 100644
1 | +# compiled output | ||
2 | +/dist | ||
3 | +/node_modules | ||
4 | +/output | ||
5 | + | ||
6 | +# config env | ||
7 | +.env | ||
8 | +.envrc | ||
9 | + | ||
10 | +# Logs | ||
11 | +logs | ||
12 | +*.log | ||
13 | +npm-debug.log* | ||
14 | +yarn-debug.log* | ||
15 | +yarn-error.log* | ||
16 | +lerna-debug.log* | ||
17 | + | ||
18 | +# OS | ||
19 | +.DS_Store | ||
20 | + | ||
21 | +# Tests | ||
22 | +/coverage | ||
23 | +/.nyc_output | ||
24 | + | ||
25 | +# IDEs and editors | ||
26 | +/.idea | ||
27 | +.project | ||
28 | +.classpath | ||
29 | +.c9/ | ||
30 | +*.launch | ||
31 | +.settings/ | ||
32 | +*.sublime-workspace | ||
33 | +builds/* | ||
34 | +nohup.out | ||
35 | + | ||
36 | +# IDE - VSCode | ||
37 | +.vscode/* | ||
38 | +!.vscode/settings.json | ||
39 | +!.vscode/tasks.json | ||
40 | +!.vscode/launch.json | ||
41 | +!.vscode/extensions.json | ||
42 | + | ||
43 | +ormconfig.json | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Backend/.gitlab-ci.yml
0 → 100644
1 | +cache: | ||
2 | + untracked: true | ||
3 | + key: '$CI_BUILD_REF_NAME' | ||
4 | + paths: | ||
5 | + - node_modules/ | ||
6 | + | ||
7 | +build: | ||
8 | + stage: build | ||
9 | + script: | ||
10 | + - npm install | ||
11 | + - npm run build | ||
12 | + only: | ||
13 | + - master | ||
14 | + tags: | ||
15 | + - build | ||
16 | + | ||
17 | +deploy: | ||
18 | + stage: deploy | ||
19 | + script: | ||
20 | + - source ./env/production.sh | ||
21 | + - pm2 start --exp-backoff-restart-delay=200 | ||
22 | + only: | ||
23 | + - master | ||
24 | + tags: | ||
25 | + - deploy |
Backend/.prettierrc
0 → 100644
Backend/README.md
0 → 100644
1 | +## Description | ||
2 | + | ||
3 | +드.론. | ||
4 | + | ||
5 | +## Installation | ||
6 | + | ||
7 | +```bash | ||
8 | +$ npm install | ||
9 | +``` | ||
10 | + | ||
11 | +## Running the app | ||
12 | + | ||
13 | +```bash | ||
14 | +# development | ||
15 | +$ npm run start | ||
16 | + | ||
17 | +# watch mode | ||
18 | +$ npm run start:dev | ||
19 | + | ||
20 | +# production mode | ||
21 | +$ npm run start:prod | ||
22 | +``` | ||
23 | + | ||
24 | +## Test | ||
25 | + | ||
26 | +```bash | ||
27 | +# unit tests | ||
28 | +$ npm run test | ||
29 | + | ||
30 | +# e2e tests | ||
31 | +$ npm run test:e2e | ||
32 | + | ||
33 | +# test coverage | ||
34 | +$ npm run test:cov | ||
35 | +``` |
Backend/backend
0 → 100644
File mode changed
Backend/ecosystem.config.js
0 → 100644
Backend/nest-cli.json
0 → 100644
Backend/package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
Backend/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "web-api", | ||
3 | + "version": "0.0.1", | ||
4 | + "description": "", | ||
5 | + "author": "", | ||
6 | + "private": true, | ||
7 | + "license": "UNLICENSED", | ||
8 | + "scripts": { | ||
9 | + "prebuild": "rimraf dist", | ||
10 | + "build": "nest build", | ||
11 | + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", | ||
12 | + "start": "nest start", | ||
13 | + "start:dev": "nest start --watch", | ||
14 | + "start:debug": "nest start --debug --watch", | ||
15 | + "start:prod": "node dist/main", | ||
16 | + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", | ||
17 | + "test": "jest", | ||
18 | + "test:watch": "jest --watch", | ||
19 | + "test:cov": "jest --coverage", | ||
20 | + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", | ||
21 | + "test:e2e": "jest --config ./test/jest-e2e.json", | ||
22 | + "typeorm": "ts-node ./node_modules/typeorm/cli.js" | ||
23 | + }, | ||
24 | + "dependencies": { | ||
25 | + "@nestjs/common": "^7.6.13", | ||
26 | + "@nestjs/config": "^0.6.3", | ||
27 | + "@nestjs/core": "^7.6.13", | ||
28 | + "@nestjs/platform-express": "^7.6.13", | ||
29 | + "@nestjs/platform-socket.io": "^7.6.15", | ||
30 | + "@nestjs/platform-ws": "^7.6.15", | ||
31 | + "@nestjs/typeorm": "^7.1.5", | ||
32 | + "@nestjs/websockets": "^7.6.15", | ||
33 | + "class-transformer": "^0.4.0", | ||
34 | + "class-validator": "^0.13.1", | ||
35 | + "pg": "^8.5.1", | ||
36 | + "reflect-metadata": "^0.1.13", | ||
37 | + "rimraf": "^3.0.2", | ||
38 | + "rxjs": "^6.6.6", | ||
39 | + "typeorm": "^0.2.32" | ||
40 | + }, | ||
41 | + "devDependencies": { | ||
42 | + "@nestjs/cli": "^7.5.6", | ||
43 | + "@nestjs/schematics": "^7.2.7", | ||
44 | + "@nestjs/testing": "^7.6.13", | ||
45 | + "@types/express": "^4.17.11", | ||
46 | + "@types/jest": "^26.0.20", | ||
47 | + "@types/node": "^14.14.31", | ||
48 | + "@types/socket.io": "^2.1.13", | ||
49 | + "@types/supertest": "^2.0.10", | ||
50 | + "@typescript-eslint/eslint-plugin": "^4.15.2", | ||
51 | + "@typescript-eslint/parser": "^4.15.2", | ||
52 | + "eslint": "^7.20.0", | ||
53 | + "eslint-config-prettier": "^8.1.0", | ||
54 | + "eslint-plugin-prettier": "^3.3.1", | ||
55 | + "jest": "^26.6.3", | ||
56 | + "prettier": "^2.2.1", | ||
57 | + "supertest": "^6.1.3", | ||
58 | + "ts-jest": "^26.5.2", | ||
59 | + "ts-loader": "^8.0.17", | ||
60 | + "ts-node": "^9.1.1", | ||
61 | + "tsconfig-paths": "^3.9.0", | ||
62 | + "typescript": "^4.1.5" | ||
63 | + }, | ||
64 | + "jest": { | ||
65 | + "moduleFileExtensions": [ | ||
66 | + "js", | ||
67 | + "json", | ||
68 | + "ts" | ||
69 | + ], | ||
70 | + "rootDir": "src", | ||
71 | + "testRegex": ".*\\.spec\\.ts$", | ||
72 | + "transform": { | ||
73 | + "^.+\\.(t|j)s$": "ts-jest" | ||
74 | + }, | ||
75 | + "collectCoverageFrom": [ | ||
76 | + "**/*.(t|j)s" | ||
77 | + ], | ||
78 | + "coverageDirectory": "../coverage", | ||
79 | + "testEnvironment": "node" | ||
80 | + } | ||
81 | +} |
Backend/src/app.module.ts
0 → 100644
1 | +import { Module } from '@nestjs/common'; | ||
2 | +import { ConfigModule, ConfigService } from '@nestjs/config'; | ||
3 | +import { TypeOrmModule } from '@nestjs/typeorm'; | ||
4 | +import configuration from './config/configuration'; | ||
5 | +import { DroneModule } from './drone/drone.module'; | ||
6 | +import { DroneEntity } from './entities/drone.entity'; | ||
7 | +import { DroneLogEntity } from './entities/drone.log.entity'; | ||
8 | +import { ScheduleEntity } from './entities/schedule.entity'; | ||
9 | +import { MemberEntity } from './entities/member.entity'; | ||
10 | +import { CodeEntity } from './entities/code.entity'; | ||
11 | +import { DroneScheduleMappingEntity } from './entities/drone.schedule.mapping.entity'; | ||
12 | + | ||
13 | +@Module({ | ||
14 | + imports: [ | ||
15 | + ConfigModule.forRoot({ | ||
16 | + isGlobal: true, | ||
17 | + load: [configuration], | ||
18 | + }), | ||
19 | + TypeOrmModule.forRootAsync({ | ||
20 | + imports: [ConfigModule], | ||
21 | + inject: [ConfigService], | ||
22 | + useFactory: (configService: ConfigService) => ({ | ||
23 | + type: 'postgres', | ||
24 | + host: configService.get('database.host'), | ||
25 | + username: configService.get('database.user'), | ||
26 | + password: configService.get('database.password'), | ||
27 | + database: configService.get('database.name'), | ||
28 | + port: configService.get('database.port'), | ||
29 | + entities: [`${__dirname}/**/*.entity.{ts,js}`], | ||
30 | + synchronize: false, | ||
31 | + }), | ||
32 | + }), | ||
33 | + DroneModule, | ||
34 | + ], | ||
35 | + controllers: [], | ||
36 | + providers: [], | ||
37 | +}) | ||
38 | +export class AppModule {} |
Backend/src/config/configuration.ts
0 → 100644
1 | +import 'dotenv/config'; | ||
2 | + | ||
3 | +export default () => ({ | ||
4 | + database: { | ||
5 | + host: process.env.DATABASE_HOST || '', | ||
6 | + user: process.env.DATABASE_USER || '', | ||
7 | + name: process.env.DATABASE_NAME || '', | ||
8 | + password: process.env.DATABASE_PASSWORD || '', | ||
9 | + port: process.env.DATABASE_PORT || '', | ||
10 | + }, | ||
11 | +}); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Backend/src/drone/drone.controller.ts
0 → 100644
This diff is collapsed. Click to expand it.
Backend/src/drone/drone.data.controller.ts
0 → 100644
1 | +import { | ||
2 | + Body, | ||
3 | + Controller, | ||
4 | + Post, | ||
5 | + UsePipes, | ||
6 | + ValidationPipe, | ||
7 | +} from '@nestjs/common'; | ||
8 | +import { DroneDataService } from 'src/drone/drone.data.service'; | ||
9 | +import { DroneApiDto } from 'src/drone/dto/drone.api.dto'; | ||
10 | + | ||
11 | +@Controller() | ||
12 | +export class DroneDataController { | ||
13 | + constructor(private droneDataService: DroneDataService) { | ||
14 | + this.droneDataService = droneDataService; | ||
15 | + } | ||
16 | + | ||
17 | + @Post('/droneLog/list') | ||
18 | + @UsePipes(new ValidationPipe({ transform: true })) | ||
19 | + async saveDroneLogList( | ||
20 | + @Body() saveDroneLogListDto: DroneApiDto.SaveDroneLogListDto, | ||
21 | + ) { | ||
22 | + await this.droneDataService.saveDroneLogList( | ||
23 | + saveDroneLogListDto.droneLogList, | ||
24 | + ); | ||
25 | + return { | ||
26 | + statusCode: 200, | ||
27 | + statusMsg: '완료', | ||
28 | + }; | ||
29 | + } | ||
30 | +} |
Backend/src/drone/drone.data.service.ts
0 → 100644
1 | +import { Injectable } from '@nestjs/common'; | ||
2 | +import { InjectRepository } from '@nestjs/typeorm'; | ||
3 | +import { DroneLogEntity } from 'src/entities/drone.log.entity'; | ||
4 | +import { DroneGateway } from 'src/drone/drone.gateway'; | ||
5 | +import { Repository } from 'typeorm/index'; | ||
6 | +import { DroneApiDto } from 'src/drone/dto/drone.api.dto'; | ||
7 | + | ||
8 | +@Injectable() | ||
9 | +export class DroneDataService { | ||
10 | + constructor( | ||
11 | + @InjectRepository(DroneLogEntity) | ||
12 | + private dronelogRepository: Repository<DroneLogEntity>, | ||
13 | + private droneGateway: DroneGateway, | ||
14 | + ) { | ||
15 | + this.dronelogRepository = dronelogRepository; | ||
16 | + this.droneGateway = droneGateway; | ||
17 | + } | ||
18 | + | ||
19 | + async saveDroneLogList(droneLogList: DroneApiDto.SaveDroneLogDto[]) { | ||
20 | + // 드론 데이터 전송 | ||
21 | + this.sendDroneLogList(droneLogList.map((log) => new DroneLogEntity(log))); | ||
22 | + | ||
23 | + // 드론로그 생성 | ||
24 | + for (const droneLog of droneLogList) { | ||
25 | + this.dronelogRepository.save({ | ||
26 | + droneId: droneLog.droneId, | ||
27 | + scheduleId: droneLog.scheduleId, | ||
28 | + latitude: droneLog.latitude, | ||
29 | + longitude: droneLog.longitude, | ||
30 | + verticalSpeed: droneLog.verticalSpeed, | ||
31 | + horizontalSpeed: droneLog.horizontalSpeed, | ||
32 | + aboveSeaLevel: droneLog.aboveSeaLevel, | ||
33 | + aboveGroundLevel: droneLog.aboveGroundLevel, | ||
34 | + }); | ||
35 | + } | ||
36 | + } | ||
37 | + | ||
38 | + sendDroneLogList(droneLogList) { | ||
39 | + // 드론데이터 전송 | ||
40 | + this.droneGateway.sendToClientsDroneLogList(droneLogList); | ||
41 | + for (const droneLog of droneLogList) { | ||
42 | + let droneLogEntity = new DroneLogEntity(droneLog); | ||
43 | + this.dronelogRepository.save(droneLogEntity); | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + async saveLog(droneLogEntity: DroneLogEntity): Promise<void> { | ||
48 | + await this.dronelogRepository.save(droneLogEntity); | ||
49 | + } | ||
50 | +} |
Backend/src/drone/drone.gateway.ts
0 → 100644
1 | +import { | ||
2 | + SubscribeMessage, | ||
3 | + WebSocketGateway, | ||
4 | + WebSocketServer, | ||
5 | +} from '@nestjs/websockets'; | ||
6 | +import { Server, Socket } from 'ws'; | ||
7 | +import { DroneService } from './drone.service'; | ||
8 | +import { DroneLogEntity } from 'src/entities/drone.log.entity'; | ||
9 | + | ||
10 | +async function sleep(ms) { | ||
11 | + return new Promise((resolve) => { | ||
12 | + setTimeout(resolve, ms); | ||
13 | + }); | ||
14 | +} | ||
15 | + | ||
16 | +@WebSocketGateway(20206, { path: '/drone' }) | ||
17 | +export class DroneGateway { | ||
18 | + constructor(private readonly droneService: DroneService) {} | ||
19 | + | ||
20 | + @WebSocketServer() | ||
21 | + server: Server; | ||
22 | + | ||
23 | + private wsClients = []; | ||
24 | + | ||
25 | + async afterInit() { | ||
26 | + let globalCircleStep = 0; | ||
27 | + while (1) { | ||
28 | + if (globalCircleStep === 3600) { | ||
29 | + globalCircleStep = 0; | ||
30 | + } else { | ||
31 | + globalCircleStep += 1; | ||
32 | + } | ||
33 | + this.wsClients.forEach((client) => { | ||
34 | + const droneTestData = this.droneService.getDroneTestData({ | ||
35 | + globalCircleStep, | ||
36 | + }); | ||
37 | + client.send( | ||
38 | + JSON.stringify( | ||
39 | + Object.assign({ | ||
40 | + data: { droneLog: droneTestData }, | ||
41 | + statusCode: 200, | ||
42 | + }), | ||
43 | + ), | ||
44 | + ); | ||
45 | + }); | ||
46 | + await sleep(1000); | ||
47 | + } | ||
48 | + } | ||
49 | + | ||
50 | + async handleConnection(client: Socket) { | ||
51 | + let clientId = this.wsClients.push(client); | ||
52 | + } | ||
53 | + | ||
54 | + handleDisconnect(client: Socket) { | ||
55 | + let clientIndex = this.wsClients.find((wsClient) => wsClient === client); | ||
56 | + if (clientIndex !== -1) { | ||
57 | + this.wsClients.splice(clientIndex, 1); | ||
58 | + } | ||
59 | + } | ||
60 | + | ||
61 | + @SubscribeMessage('') | ||
62 | + handleMessages(client: Socket, payload: { name: string; text: string }) { | ||
63 | + client.send( | ||
64 | + JSON.stringify({ | ||
65 | + number: 10, | ||
66 | + member: 'tony', | ||
67 | + }), | ||
68 | + ); | ||
69 | + } | ||
70 | + | ||
71 | + sendToClientsDroneLogList(droneLogList: Array<DroneLogEntity>) { | ||
72 | + this.wsClients.forEach((client) => { | ||
73 | + client.send( | ||
74 | + JSON.stringify( | ||
75 | + Object.assign({ | ||
76 | + data: { droneLog: droneLogList }, | ||
77 | + statusCode: 200, | ||
78 | + }), | ||
79 | + ), | ||
80 | + ); | ||
81 | + }); | ||
82 | + } | ||
83 | +} |
Backend/src/drone/drone.module.ts
0 → 100644
1 | +import { Module } from '@nestjs/common'; | ||
2 | +import { TypeOrmModule } from '@nestjs/typeorm'; | ||
3 | +import { DroneController } from './drone.controller'; | ||
4 | +import { DroneService } from './drone.service'; | ||
5 | +import { DroneDataController } from 'src/drone/drone.data.controller'; | ||
6 | +import { DroneDataService } from './drone.data.service'; | ||
7 | +import { DroneGateway } from './drone.gateway'; | ||
8 | +import { DroneEntity } from 'src/entities/drone.entity'; | ||
9 | +import { ScheduleEntity } from 'src/entities/schedule.entity'; | ||
10 | +import { DroneLogEntity } from 'src/entities/drone.log.entity'; | ||
11 | +import { MemberEntity } from 'src/entities/member.entity'; | ||
12 | +import { CodeEntity } from 'src/entities/code.entity'; | ||
13 | +import { DroneScheduleMappingEntity } from 'src/entities/drone.schedule.mapping.entity'; | ||
14 | + | ||
15 | +@Module({ | ||
16 | + imports: [ | ||
17 | + TypeOrmModule.forFeature([ | ||
18 | + MemberEntity, | ||
19 | + DroneEntity, | ||
20 | + DroneLogEntity, | ||
21 | + ScheduleEntity, | ||
22 | + CodeEntity, | ||
23 | + DroneScheduleMappingEntity, | ||
24 | + ]), | ||
25 | + ], | ||
26 | + controllers: [DroneController, DroneDataController], | ||
27 | + providers: [DroneGateway, DroneService, DroneDataService], | ||
28 | +}) | ||
29 | +export class DroneModule {} |
Backend/src/drone/drone.service.ts
0 → 100644
This diff is collapsed. Click to expand it.
Backend/src/drone/dto/drone.api.dto.ts
0 → 100644
1 | +import { | ||
2 | + IsArray, | ||
3 | + IsNotEmpty, | ||
4 | + IsOptional, | ||
5 | + IsNumber, | ||
6 | + IsString, | ||
7 | + ValidateNested, | ||
8 | + IsDateString, | ||
9 | +} from 'class-validator'; | ||
10 | +import { Type } from 'class-transformer'; | ||
11 | +import { DroneEntity } from 'src/entities/drone.entity'; | ||
12 | + | ||
13 | +export namespace DroneApiDto { | ||
14 | + export class SaveDroneListDto { | ||
15 | + @IsArray() | ||
16 | + @ValidateNested({ each: true }) | ||
17 | + @Type(() => DroneEntity) | ||
18 | + droneList: DroneEntity[]; | ||
19 | + } | ||
20 | + | ||
21 | + export class UpdateDroneListDto { | ||
22 | + @IsArray() | ||
23 | + @ValidateNested({ each: true }) | ||
24 | + @Type(() => UpdateDroneDto) | ||
25 | + droneList: UpdateDroneDto[]; | ||
26 | + } | ||
27 | + | ||
28 | + export class UpdateDroneDto { | ||
29 | + @IsNotEmpty() | ||
30 | + @IsNumber() | ||
31 | + id: number; | ||
32 | + | ||
33 | + @IsString() | ||
34 | + @IsOptional() | ||
35 | + maker: string; | ||
36 | + | ||
37 | + @IsString() | ||
38 | + @IsOptional() | ||
39 | + usage: string; | ||
40 | + | ||
41 | + @IsNumber() | ||
42 | + @IsOptional() | ||
43 | + specification: number; | ||
44 | + | ||
45 | + @IsNumber() | ||
46 | + @IsOptional() | ||
47 | + weight: number; | ||
48 | + } | ||
49 | + | ||
50 | + export class SaveSchduleListDto { | ||
51 | + @IsArray() | ||
52 | + @ValidateNested({ each: true }) | ||
53 | + @Type(() => SaveSchduleDto) | ||
54 | + schduleList: SaveSchduleDto[]; | ||
55 | + } | ||
56 | + | ||
57 | + export class SaveSchduleDto { | ||
58 | + @IsNotEmpty() | ||
59 | + @IsNumber() | ||
60 | + droneId: number; | ||
61 | + | ||
62 | + @IsDateString() | ||
63 | + @IsNotEmpty() | ||
64 | + startTime: string; | ||
65 | + | ||
66 | + @IsDateString() | ||
67 | + @IsNotEmpty() | ||
68 | + terminateTime: string; | ||
69 | + | ||
70 | + @IsNumber() | ||
71 | + @IsNotEmpty() | ||
72 | + startLatitude: number; | ||
73 | + | ||
74 | + @IsNumber() | ||
75 | + @IsNotEmpty() | ||
76 | + startLongitude: number; | ||
77 | + | ||
78 | + @IsNumber() | ||
79 | + @IsNotEmpty() | ||
80 | + terminateLatitude: number; | ||
81 | + | ||
82 | + @IsNumber() | ||
83 | + @IsNotEmpty() | ||
84 | + terminateLongitude: number; | ||
85 | + } | ||
86 | + | ||
87 | + export class UpdateSchduleListDto { | ||
88 | + @IsArray() | ||
89 | + @ValidateNested({ each: true }) | ||
90 | + @Type(() => UpdateSchduleDto) | ||
91 | + schduleList: UpdateSchduleDto[]; | ||
92 | + } | ||
93 | + | ||
94 | + export class UpdateSchduleDto { | ||
95 | + @IsNotEmpty() | ||
96 | + @IsNumber() | ||
97 | + id: number; | ||
98 | + | ||
99 | + @IsDateString() | ||
100 | + @IsNotEmpty() | ||
101 | + startTime: string; | ||
102 | + | ||
103 | + @IsDateString() | ||
104 | + @IsNotEmpty() | ||
105 | + terminateTime: string; | ||
106 | + | ||
107 | + @IsNumber() | ||
108 | + @IsNotEmpty() | ||
109 | + startLatitude: number; | ||
110 | + | ||
111 | + @IsNumber() | ||
112 | + @IsNotEmpty() | ||
113 | + startLongitude: number; | ||
114 | + | ||
115 | + @IsNumber() | ||
116 | + @IsNotEmpty() | ||
117 | + terminateLatitude: number; | ||
118 | + | ||
119 | + @IsNumber() | ||
120 | + @IsNotEmpty() | ||
121 | + terminateLongitude: number; | ||
122 | + } | ||
123 | + | ||
124 | + export class SaveDroneLogListDto { | ||
125 | + @IsArray() | ||
126 | + @ValidateNested({ each: true }) | ||
127 | + @Type(() => SaveDroneLogDto) | ||
128 | + droneLogList: SaveDroneLogDto[]; | ||
129 | + } | ||
130 | + | ||
131 | + export class SaveDroneLogDto { | ||
132 | + @IsNotEmpty() | ||
133 | + @IsNumber() | ||
134 | + droneId: number; | ||
135 | + | ||
136 | + @IsNotEmpty() | ||
137 | + @IsNumber() | ||
138 | + scheduleId: number; | ||
139 | + | ||
140 | + @IsNotEmpty() | ||
141 | + @IsNumber() | ||
142 | + latitude: number; | ||
143 | + | ||
144 | + @IsNotEmpty() | ||
145 | + @IsNumber() | ||
146 | + longitude: number; | ||
147 | + | ||
148 | + @IsNotEmpty() | ||
149 | + @IsNumber() | ||
150 | + verticalSpeed: number; | ||
151 | + | ||
152 | + @IsNotEmpty() | ||
153 | + @IsNumber() | ||
154 | + horizontalSpeed: number; | ||
155 | + | ||
156 | + @IsNotEmpty() | ||
157 | + @IsNumber() | ||
158 | + aboveSeaLevel: number; | ||
159 | + | ||
160 | + @IsNotEmpty() | ||
161 | + @IsNumber() | ||
162 | + aboveGroundLevel: number; | ||
163 | + } | ||
164 | +} |
Backend/src/drone/dto/drone.dto.ts
0 → 100644
1 | +export class DroneDto { | ||
2 | + private _id: number; | ||
3 | + private _model_name: string; | ||
4 | + private _maker: string; | ||
5 | + private _usage: string; | ||
6 | + private _picture: string; | ||
7 | + private _specification: number; | ||
8 | + private _weight: number; | ||
9 | + | ||
10 | + constructor( | ||
11 | + id: number, | ||
12 | + model_name: string, | ||
13 | + maker: string, | ||
14 | + usage: string, | ||
15 | + picture: string, | ||
16 | + specification: number, | ||
17 | + weight: number, | ||
18 | + ) { | ||
19 | + this._id = id; | ||
20 | + this._model_name = model_name; | ||
21 | + this._maker = maker; | ||
22 | + this._usage = usage; | ||
23 | + this._picture = picture; | ||
24 | + this._specification = specification; | ||
25 | + this._weight = weight; | ||
26 | + } | ||
27 | + | ||
28 | + get id(): number { | ||
29 | + return this._id; | ||
30 | + } | ||
31 | + set id(value: number) { | ||
32 | + this._id = value; | ||
33 | + } | ||
34 | + | ||
35 | + get model_name(): string { | ||
36 | + return this._model_name; | ||
37 | + } | ||
38 | + set model_name(value: string) { | ||
39 | + this._model_name = value; | ||
40 | + } | ||
41 | + | ||
42 | + get maker(): string { | ||
43 | + return this._maker; | ||
44 | + } | ||
45 | + set maker(value: string) { | ||
46 | + this._maker = value; | ||
47 | + } | ||
48 | + | ||
49 | + get usage(): string { | ||
50 | + return this._usage; | ||
51 | + } | ||
52 | + set usage(value: string) { | ||
53 | + this._usage = value; | ||
54 | + } | ||
55 | + | ||
56 | + get picture(): string { | ||
57 | + return this._picture; | ||
58 | + } | ||
59 | + set picture(value: string) { | ||
60 | + this._picture = value; | ||
61 | + } | ||
62 | + | ||
63 | + get specification(): number { | ||
64 | + return this._specification; | ||
65 | + } | ||
66 | + set specification(value: number) { | ||
67 | + this._specification = value; | ||
68 | + } | ||
69 | + | ||
70 | + get weight(): number { | ||
71 | + return this._weight; | ||
72 | + } | ||
73 | + set weight(value: number) { | ||
74 | + this._weight = value; | ||
75 | + } | ||
76 | +} |
Backend/src/drone/dto/droneLog.dto.ts
0 → 100644
1 | +export class DroneLogDto { | ||
2 | + constructor(props) { | ||
3 | + // props.id = props.id ? parseInt(props.id) : props.id; | ||
4 | + props.droneId = props.droneId ? parseInt(props.droneId) : props.droneId; | ||
5 | + props.scheduleId = props.scheduleId | ||
6 | + ? parseInt(props.scheduleId) | ||
7 | + : props.scheduleId; | ||
8 | + Object.assign(this, props); | ||
9 | + } | ||
10 | + id: number; | ||
11 | + droneId: number; | ||
12 | + scheduleId: number; | ||
13 | + latitude: number; | ||
14 | + longitude: number; | ||
15 | + verticalSpeed: number; | ||
16 | + horizontalSpeed: number; | ||
17 | + aboveSeaLevel: number; | ||
18 | + aboveGroundLevel: number; | ||
19 | +} |
Backend/src/entities/code.entity.ts
0 → 100644
1 | +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index'; | ||
2 | + | ||
3 | +@Entity({ name: 'code', schema: 'public' }) | ||
4 | +export class CodeEntity { | ||
5 | + @PrimaryGeneratedColumn({ name: 'id' }) | ||
6 | + id: number; | ||
7 | + | ||
8 | + @Column({ length: 20, name: 'code_group' }) | ||
9 | + code_Group: string; | ||
10 | + | ||
11 | + @Column({ length: 20, name: 'code_group_name' }) | ||
12 | + codeGroupName: string; | ||
13 | + | ||
14 | + @Column({ length: 20, name: 'code_text' }) | ||
15 | + codeText: string; | ||
16 | + | ||
17 | + @Column({ length: 20, name: 'code_value' }) | ||
18 | + codeValue: string; | ||
19 | + | ||
20 | + @Column({ length: 20, name: 'code_value_name' }) | ||
21 | + codeValueName: string; | ||
22 | +} |
Backend/src/entities/drone.entity.ts
0 → 100644
1 | +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index'; | ||
2 | + | ||
3 | +@Entity({ name: 'drone', schema: 'public' }) | ||
4 | +export class DroneEntity { | ||
5 | + @PrimaryGeneratedColumn({ name: 'id' }) | ||
6 | + id: number; | ||
7 | + | ||
8 | + @Column({ length: 20, name: 'model_name' }) | ||
9 | + modelName: string; | ||
10 | + | ||
11 | + @Column({ length: 20 }) | ||
12 | + maker: string; | ||
13 | + | ||
14 | + @Column({ length: 20 }) | ||
15 | + usage: string; | ||
16 | + | ||
17 | + @Column({ length: 20, name: 'usagename' }) | ||
18 | + usageName: string; | ||
19 | + | ||
20 | + @Column({ length: 20 }) | ||
21 | + picture: string; | ||
22 | + | ||
23 | + @Column() | ||
24 | + specification: number; | ||
25 | + | ||
26 | + @Column() | ||
27 | + weight: number; | ||
28 | +} |
Backend/src/entities/drone.log.entity.ts
0 → 100644
1 | +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index'; | ||
2 | + | ||
3 | +@Entity({ name: 'drone_log', schema: 'public' }) | ||
4 | +export class DroneLogEntity { | ||
5 | + @PrimaryGeneratedColumn({ name: 'id' }) | ||
6 | + id: number; | ||
7 | + | ||
8 | + @Column({ name: 'drone_id' }) | ||
9 | + droneId: number; | ||
10 | + | ||
11 | + @Column({ name: 'schedule_id' }) | ||
12 | + scheduleId: number; | ||
13 | + | ||
14 | + @Column() | ||
15 | + latitude: number; | ||
16 | + | ||
17 | + @Column() | ||
18 | + longitude: number; | ||
19 | + | ||
20 | + @Column({ name: 'vertical_speed' }) | ||
21 | + verticalSpeed: number; | ||
22 | + | ||
23 | + @Column({ name: 'horizontal_speed' }) | ||
24 | + horizontalSpeed: number; | ||
25 | + | ||
26 | + @Column({ name: 'above_sea_level' }) | ||
27 | + aboveSeaLevel: number; | ||
28 | + | ||
29 | + @Column({ name: 'above_ground_level' }) | ||
30 | + aboveGroundLevel: number; | ||
31 | + | ||
32 | + @Column({ type: 'timestamp', name: 'created_at' }) | ||
33 | + createdAt: Date; | ||
34 | + | ||
35 | + constructor(props) { | ||
36 | + Object.assign(this, props); | ||
37 | + } | ||
38 | +} |
1 | +import { Entity, PrimaryGeneratedColumn } from 'typeorm/index'; | ||
2 | + | ||
3 | +@Entity({ name: 'drone_schedule_mapping', schema: 'public' }) | ||
4 | +export class DroneScheduleMappingEntity { | ||
5 | + @PrimaryGeneratedColumn({ name: 'drone_id' }) | ||
6 | + droneId: number; | ||
7 | + | ||
8 | + @PrimaryGeneratedColumn({ name: 'schedule_id' }) | ||
9 | + scheduleId: number; | ||
10 | +} |
Backend/src/entities/member.entity.ts
0 → 100644
1 | +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index'; | ||
2 | + | ||
3 | +@Entity({ name: 'member', schema: 'public' }) | ||
4 | +export class MemberEntity { | ||
5 | + @PrimaryGeneratedColumn({ name: 'id' }) | ||
6 | + id: number; | ||
7 | + | ||
8 | + @Column({ length: 15 }) | ||
9 | + name: string; | ||
10 | + | ||
11 | + @Column({ length: 20, name:'tel_number' }) | ||
12 | + telNumber: string; | ||
13 | + | ||
14 | + @Column({ length: 20 }) | ||
15 | + affiliation: string; | ||
16 | +} |
Backend/src/entities/schedule.entity.ts
0 → 100644
1 | +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index'; | ||
2 | + | ||
3 | +@Entity({ name: 'schedule', schema: 'public' }) | ||
4 | +export class ScheduleEntity { | ||
5 | + @PrimaryGeneratedColumn({ name: 'id' }) | ||
6 | + id: number; | ||
7 | + | ||
8 | + @Column({ type: 'timestamp', name: 'start_time' }) | ||
9 | + startTime: Date; | ||
10 | + | ||
11 | + @Column({ type: 'timestamp', name: 'terminate_time' }) | ||
12 | + terminateTime: Date; | ||
13 | + | ||
14 | + @Column({ name: 'start_latitude' }) | ||
15 | + startLatitude: number; | ||
16 | + | ||
17 | + @Column({ name: 'start_longitude' }) | ||
18 | + startLongitude: number; | ||
19 | + | ||
20 | + @Column({ name: 'terminate_latitude' }) | ||
21 | + terminateLatitude: number; | ||
22 | + | ||
23 | + @Column({ name: 'terminate_longitude' }) | ||
24 | + terminateLongitude: number; | ||
25 | +} |
Backend/src/main.ts
0 → 100644
1 | +import { NestFactory } from '@nestjs/core'; | ||
2 | +import { WsAdapter } from '@nestjs/platform-ws'; | ||
3 | +import { AppModule } from './app.module'; | ||
4 | + | ||
5 | +async function bootstrap() { | ||
6 | + const app = await NestFactory.create(AppModule); | ||
7 | + app.useWebSocketAdapter(new WsAdapter(app)); | ||
8 | + await app.listen(20205); | ||
9 | +} | ||
10 | +bootstrap(); |
Backend/test/app.e2e-spec.ts
0 → 100644
1 | +import { Test, TestingModule } from '@nestjs/testing'; | ||
2 | +import { INestApplication } from '@nestjs/common'; | ||
3 | +import * as request from 'supertest'; | ||
4 | +import { AppModule } from './../src/app.module'; | ||
5 | + | ||
6 | +describe('AppController (e2e)', () => { | ||
7 | + let app: INestApplication; | ||
8 | + | ||
9 | + beforeEach(async () => { | ||
10 | + const moduleFixture: TestingModule = await Test.createTestingModule({ | ||
11 | + imports: [AppModule], | ||
12 | + }).compile(); | ||
13 | + | ||
14 | + app = moduleFixture.createNestApplication(); | ||
15 | + await app.init(); | ||
16 | + }); | ||
17 | + | ||
18 | + it('/ (GET)', () => { | ||
19 | + return request(app.getHttpServer()) | ||
20 | + .get('/') | ||
21 | + .expect(200) | ||
22 | + .expect('Hello World!'); | ||
23 | + }); | ||
24 | +}); |
Backend/test/jest-e2e.json
0 → 100644
Backend/tsconfig.build.json
0 → 100644
Backend/tsconfig.json
0 → 100644
1 | +{ | ||
2 | + "compilerOptions": { | ||
3 | + "module": "commonjs", | ||
4 | + "declaration": true, | ||
5 | + "removeComments": true, | ||
6 | + "emitDecoratorMetadata": true, | ||
7 | + "experimentalDecorators": true, | ||
8 | + "allowSyntheticDefaultImports": true, | ||
9 | + "target": "es2017", | ||
10 | + "sourceMap": true, | ||
11 | + "outDir": "./dist", | ||
12 | + "baseUrl": "./", | ||
13 | + "incremental": true | ||
14 | + } | ||
15 | +} |
Frontend/.editorconfig
0 → 100644
Frontend/.eslintignore
0 → 100644
Frontend/.eslintrc.js
0 → 100644
1 | +module.exports = { | ||
2 | + root: true, | ||
3 | + env: { | ||
4 | + browser: true, | ||
5 | + node: true, | ||
6 | + }, | ||
7 | + parserOptions: { | ||
8 | + parser: 'babel-eslint', | ||
9 | + }, | ||
10 | + extends: [ | ||
11 | + 'plugin:vue/essential', | ||
12 | + '@vue/airbnb', | ||
13 | + ], | ||
14 | + plugins: [ | ||
15 | + ], | ||
16 | + // add your custom rules here | ||
17 | + rules: { | ||
18 | + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', | ||
19 | + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', | ||
20 | + 'no-unused-vars': 'warn', | ||
21 | + 'comma-dangle': ['error', 'always-multiline'], | ||
22 | + 'linebreak-style': 0, | ||
23 | + 'import/no-extraneous-dependencies': 0, | ||
24 | + 'no-shadow': 0, | ||
25 | + 'import/prefer-default-export': 0, | ||
26 | + 'max-len': ['warn', { code: 200 }], | ||
27 | + 'import/extensions': ['error', 'always', { | ||
28 | + js: 'never', | ||
29 | + jsx: 'never', | ||
30 | + vue: 'never', | ||
31 | + }], | ||
32 | + indent: [2, 2], | ||
33 | + }, | ||
34 | + settings: { | ||
35 | + 'import/extensions': ['.js', '.jsx', '.vue'], | ||
36 | + 'import/resolver': { | ||
37 | + alias: { | ||
38 | + map: [ | ||
39 | + ['@', './src'], | ||
40 | + ], | ||
41 | + extensions: ['.js', '.vue', '.jsx'], | ||
42 | + }, | ||
43 | + node: { | ||
44 | + extensions: ['.js', '.vue', '.jsx'], | ||
45 | + }, | ||
46 | + }, | ||
47 | + }, | ||
48 | +}; |
Frontend/.gitignore
0 → 100644
1 | +# Created by .ignore support plugin (hsz.mobi) | ||
2 | +### Node template | ||
3 | +# Logs | ||
4 | +/logs | ||
5 | +*.log | ||
6 | +npm-debug.log* | ||
7 | +yarn-debug.log* | ||
8 | +yarn-error.log* | ||
9 | + | ||
10 | +# Runtime data | ||
11 | +pids | ||
12 | +*.pid | ||
13 | +*.seed | ||
14 | +*.pid.lock | ||
15 | + | ||
16 | +# Directory for instrumented libs generated by jscoverage/JSCover | ||
17 | +lib-cov | ||
18 | + | ||
19 | +# Coverage directory used by tools like istanbul | ||
20 | +coverage | ||
21 | + | ||
22 | +# nyc test coverage | ||
23 | +.nyc_output | ||
24 | + | ||
25 | +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | ||
26 | +.grunt | ||
27 | + | ||
28 | +# Bower dependency directory (https://bower.io/) | ||
29 | +bower_components | ||
30 | + | ||
31 | +# node-waf configuration | ||
32 | +.lock-wscript | ||
33 | + | ||
34 | +# Compiled binary addons (https://nodejs.org/api/addons.html) | ||
35 | +build/Release | ||
36 | + | ||
37 | +# Dependency directories | ||
38 | +node_modules/ | ||
39 | +jspm_packages/ | ||
40 | + | ||
41 | +# TypeScript v1 declaration files | ||
42 | +typings/ | ||
43 | + | ||
44 | +# Optional npm cache directory | ||
45 | +.npm | ||
46 | + | ||
47 | +# Optional eslint cache | ||
48 | +.eslintcache | ||
49 | + | ||
50 | +# Optional REPL history | ||
51 | +.node_repl_history | ||
52 | + | ||
53 | +# Output of 'npm pack' | ||
54 | +*.tgz | ||
55 | + | ||
56 | +# Yarn Integrity file | ||
57 | +.yarn-integrity | ||
58 | + | ||
59 | +# parcel-bundler cache (https://parceljs.org/) | ||
60 | +.cache | ||
61 | + | ||
62 | +# next.js build output | ||
63 | +.next | ||
64 | + | ||
65 | +# nuxt.js build output | ||
66 | +.nuxt | ||
67 | + | ||
68 | +# Nuxt generate | ||
69 | +dist | ||
70 | + | ||
71 | +# vuepress build output | ||
72 | +.vuepress/dist | ||
73 | + | ||
74 | +# Serverless directories | ||
75 | +.serverless | ||
76 | + | ||
77 | +# IDE / Editor | ||
78 | +.idea | ||
79 | + | ||
80 | +# Service worker | ||
81 | +sw.* | ||
82 | + | ||
83 | +# macOS | ||
84 | +.DS_Store | ||
85 | + | ||
86 | +# Vim swap files | ||
87 | +*.swp | ||
88 | +.DS_Store |
Frontend/.gitlab-ci.yml
0 → 100755
1 | +stages: | ||
2 | + - others | ||
3 | + - build | ||
4 | + - deploy | ||
5 | + | ||
6 | +.build: | ||
7 | + cache: | ||
8 | + paths: | ||
9 | + - node_modules/ | ||
10 | + | ||
11 | +.deploy: | ||
12 | + variables: | ||
13 | + GIT_STRATEGY: none | ||
14 | + | ||
15 | +dev_build: | ||
16 | + stage: build | ||
17 | + extends: .build | ||
18 | + script: | ||
19 | + - yarn install | ||
20 | + - yarn build | ||
21 | + only: | ||
22 | + - develop | ||
23 | + tags: | ||
24 | + - front | ||
25 | + | ||
26 | +dev_deploy: | ||
27 | + stage: deploy | ||
28 | + extends: .deploy | ||
29 | + script: | ||
30 | + - pm2 start --exp-backoff-restart-delay=100 | ||
31 | + only: | ||
32 | + - develop | ||
33 | + tags: | ||
34 | + - front | ||
35 | +#(DEV) mockserver: | ||
36 | +# stage: deploy | ||
37 | +# extends: .deploy | ||
38 | +# image: node:15 | ||
39 | +# script: yarn serve | ||
40 | +# only: | ||
41 | +# - develop | ||
42 | +# artifacts: | ||
43 | +# paths: | ||
44 | +# - ./ | ||
45 | +# tags: | ||
46 | +# - ws-server | ||
47 | +# | ||
48 | +#(DEV) websocket: | ||
49 | +# stage: deploy | ||
50 | +# extends: .deploy | ||
51 | +# image: docker:latest | ||
52 | +# script: yarn ws | ||
53 | +# only: | ||
54 | +# - develop | ||
55 | +# artifacts: | ||
56 | +# paths: | ||
57 | +# - ./ | ||
58 | +# tags: | ||
59 | +# - api-server |
Frontend/.prettierrc.json
0 → 100644
Frontend/Readme.md
0 → 100644
1 | +# drone-web-nuxt | ||
2 | + | ||
3 | +## Build Setup | ||
4 | + | ||
5 | +```bash | ||
6 | +# install dependencies | ||
7 | +$ yarn install | ||
8 | + | ||
9 | +# serve with hot reload at localhost:3000 | ||
10 | +$ yarn dev | ||
11 | + | ||
12 | +# build for production and launch server | ||
13 | +$ yarn build | ||
14 | +$ yarn start | ||
15 | + | ||
16 | +# generate static project | ||
17 | +$ yarn generate | ||
18 | +``` | ||
19 | + | ||
20 | +For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). |
Frontend/deploy.sh
0 → 100644
Frontend/ecosystem.config.js
0 → 100644
1 | +module.exports = { | ||
2 | + apps: [ | ||
3 | + { | ||
4 | + name: 'drone-front', | ||
5 | + // package.json에 정의된 npm run start를 실행하게 해서 PM2로 관리하게 한다. | ||
6 | + script: 'yarn', | ||
7 | + args: 'run start', | ||
8 | + instances: '1', | ||
9 | + max_memory_restart: '1G', | ||
10 | + error_file: 'err.log', | ||
11 | + out_file: 'out.log', | ||
12 | + log_file: 'combined.log', | ||
13 | + }, | ||
14 | + ], | ||
15 | +}; |
Frontend/front.service
0 → 100644
1 | +[Unit] | ||
2 | +Description=Drone front service | ||
3 | +After=network.target | ||
4 | +StartLimitIntervalSec=0 | ||
5 | + | ||
6 | +[Service] | ||
7 | +Type=simple | ||
8 | +Restart=always | ||
9 | +RestartSec=1 | ||
10 | +User=gitlab-runner | ||
11 | +ExecStart=/home/gitlab-runner/builds/4hhEfxWU/0/khu-oz-wizard/drone-monitoring-web-ui yarn start | ||
12 | + | ||
13 | +[Install] | ||
14 | +WantedBy=multi-user.target |
Frontend/lib/db.json
0 → 100644
This diff could not be displayed because it is too large.
Frontend/nginx
0 → 100644
1 | +map $sent_http_content_type $expires { | ||
2 | + "text/html" epoch; | ||
3 | + "text/html; charset=utf-8" epoch; | ||
4 | + default off; | ||
5 | +} | ||
6 | + | ||
7 | +server { | ||
8 | + listen 20205; | ||
9 | + server_name localhost; | ||
10 | + | ||
11 | + gzip on; | ||
12 | + gzip_types text/plain application/xml text/css application/javascript; | ||
13 | + gzip_min_length 1000; | ||
14 | + | ||
15 | + location / { | ||
16 | + expires $expires; | ||
17 | + | ||
18 | + proxy_redirect off; | ||
19 | + proxy_set_header Host $host; | ||
20 | + proxy_set_header X-Real-IP $remote_addr; | ||
21 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
22 | + proxy_set_header X-Forwarded-Proto $scheme; | ||
23 | + proxy_read_timeout 1m; | ||
24 | + proxy_connect_timeout 1m; | ||
25 | + proxy_pass http://127.0.0.1:3000; | ||
26 | + } | ||
27 | +} |
Frontend/nuxt.config.js
0 → 100644
1 | +/* eslint-disable no-unused-vars */ | ||
2 | +import api from './nuxtConfig/api'; | ||
3 | +import build from './nuxtConfig/build'; | ||
4 | +import theme from './nuxtConfig/theme'; | ||
5 | +import nuxtConfigModule from './nuxtConfig/module'; | ||
6 | +import io from './nuxtConfig/ioConfig'; | ||
7 | +import extendRouter from './nuxtConfig/extendRouter'; | ||
8 | + | ||
9 | +// 설정 내용이 짧은 것 및 구조화 하기 애매한 것은 별도 파일로 관리하지 않음. | ||
10 | +export default { | ||
11 | + // Global page headers: https://go.nuxtjs.dev/config-head | ||
12 | + head: { | ||
13 | + title: 'drone-web-nuxt', | ||
14 | + htmlAttrs: { | ||
15 | + lang: 'en', | ||
16 | + }, | ||
17 | + meta: [ | ||
18 | + { charset: 'utf-8' }, | ||
19 | + { name: 'viewport', content: 'width=device-width, initial-scale=1' }, | ||
20 | + { hid: 'description', name: 'description', content: '' }, | ||
21 | + ], | ||
22 | + link: [ | ||
23 | + { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, | ||
24 | + ], | ||
25 | + }, | ||
26 | + | ||
27 | + // Auto import components: https://go.nuxtjs.dev/config-components | ||
28 | + components: false, | ||
29 | + // source Directory | ||
30 | + srcDir: 'src/', | ||
31 | + | ||
32 | + /* middleware */ | ||
33 | + serverMiddleware: [ | ||
34 | + './serverMiddleWare/index', | ||
35 | + ], | ||
36 | + router: { | ||
37 | + // router middleware | ||
38 | + middleware: 'router', | ||
39 | + // router extend | ||
40 | + // extendRoutes: extendRouter, | ||
41 | + }, | ||
42 | + | ||
43 | + // module, plugin, alias, robots | ||
44 | + ...nuxtConfigModule, | ||
45 | + // axios, proxy, auth | ||
46 | + ...api, | ||
47 | + // env, runtimeConfig, build | ||
48 | + ...build, | ||
49 | + // loading, transition, css | ||
50 | + ...theme, | ||
51 | + | ||
52 | + // vue Global Config | ||
53 | + vue: { | ||
54 | + config: { | ||
55 | + productionTip: true, | ||
56 | + devtools: process.env.NODE_ENV === 'development', | ||
57 | + // silent: process.env.NODE_ENV !== 'development', | ||
58 | + // performance: process.env.NODE_ENV === 'development', | ||
59 | + }, | ||
60 | + }, | ||
61 | + | ||
62 | + // robots Setting | ||
63 | + robots: { | ||
64 | + UserAgent: '*', | ||
65 | + Disallow: '/', | ||
66 | + }, | ||
67 | + // socket io Setting | ||
68 | + io, | ||
69 | +}; |
Frontend/nuxtConfig/api.js
0 → 100644
1 | +import { version } from '../package.json'; | ||
2 | + | ||
3 | +/** | ||
4 | + * api 관련된 nuxt 옵션을 정리합니다. | ||
5 | + * 해당 옵션은 아래와 같습니다. | ||
6 | + * auth, axios, proxy, | ||
7 | + */ | ||
8 | +export default { | ||
9 | + // Axios module configuration: https://go.nuxtjs.dev/config-axios | ||
10 | + axios: { | ||
11 | + proxy: true, | ||
12 | + retry: { retries: 3 }, | ||
13 | + // baseUrl: 'http://localhost:5555', | ||
14 | + headers: { | ||
15 | + common: { | ||
16 | + Accept: 'application/json, text/plain, */*', | ||
17 | + AppVersion: version, | ||
18 | + }, | ||
19 | + delete: {}, | ||
20 | + get: {}, | ||
21 | + head: {}, | ||
22 | + post: {}, | ||
23 | + put: {}, | ||
24 | + patch: {}, | ||
25 | + }, | ||
26 | + }, | ||
27 | + proxy: { | ||
28 | + '/api': { | ||
29 | + target: process.env.BASE_API_URL || 'http://14.33.35.148:20205', | ||
30 | + pathRewrite: { | ||
31 | + '^/api': '', | ||
32 | + }, | ||
33 | + changeOrigin: true, | ||
34 | + }, | ||
35 | + }, | ||
36 | + auth: { | ||
37 | + // Options | ||
38 | + }, | ||
39 | +}; |
Frontend/nuxtConfig/build.js
0 → 100644
1 | +/** | ||
2 | + * 빌드에 관련된 nuxt 옵션을 정리합니다. | ||
3 | + * 해당 옵션은 아래와 같습니다. | ||
4 | + * env, build, | ||
5 | + */ | ||
6 | +export default { | ||
7 | + // modern property https://ko.nuxtjs.org/docs/2.x/configuration-glossary/configuration-modern | ||
8 | + modern: false, | ||
9 | + /* env Setting */ | ||
10 | + env: { | ||
11 | + BASE_API_URL: process.env.BASE_API_URL, | ||
12 | + BASE_APP_URL: process.env.BASE_APP_URL, | ||
13 | + BASE_I18N_LOCALE: process.env.BASE_I18N_LOCALE, | ||
14 | + BASE_I18N_FALLBACK_LOCALE: process.env.BASE_I18N_FALLBACK_LOCALE, | ||
15 | + }, | ||
16 | + // public nuxt.context config variables | ||
17 | + publicRuntimeConfig: { | ||
18 | + BASE_API_URL: process.env.BASE_API_URL, | ||
19 | + BASE_APP_URL: process.env.BASE_APP_URL, | ||
20 | + BASE_I18N_LOCALE: process.env.BASE_I18N_LOCALE, | ||
21 | + BASE_I18N_FALLBACK_LOCALE: process.env.BASE_I18N_FALLBACK_LOCALE, | ||
22 | + }, | ||
23 | + // private nuxt.context config variables | ||
24 | + privateRuntimeConfig: { | ||
25 | + | ||
26 | + }, | ||
27 | + | ||
28 | + // Build Configuration: https://go.nuxtjs.dev/config-build | ||
29 | + build: { | ||
30 | + loaders: { | ||
31 | + vue: { | ||
32 | + transformAssetUrls: { | ||
33 | + 'vl-style-icon': 'src', | ||
34 | + }, | ||
35 | + }, | ||
36 | + // for Antdv CustomTheme Setting | ||
37 | + less: { | ||
38 | + lessOptions: { | ||
39 | + javascriptEnabled: true, | ||
40 | + math: 'always', | ||
41 | + }, | ||
42 | + }, | ||
43 | + }, | ||
44 | + devtool: true, | ||
45 | + analyze: true, | ||
46 | + }, | ||
47 | + // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules | ||
48 | + buildModules: [ | ||
49 | + // https://go.nuxtjs.dev/eslint | ||
50 | + '@nuxt/image', | ||
51 | + ], | ||
52 | + image: { | ||
53 | + staticFilename: '[publicPath]/images/[name]-[hash][ext]', | ||
54 | + presets: { | ||
55 | + avatar: { | ||
56 | + modifiers: { | ||
57 | + format: 'jpg', | ||
58 | + width: 50, | ||
59 | + height: 50, | ||
60 | + }, | ||
61 | + }, | ||
62 | + }, | ||
63 | + }, | ||
64 | +}; |
Frontend/nuxtConfig/extendRouter.js
0 → 100644
Frontend/nuxtConfig/ioConfig.js
0 → 100644
Frontend/nuxtConfig/module.js
0 → 100644
1 | +/** | ||
2 | + * 모듈에 관련된 nuxt 옵션을 정리합니다. | ||
3 | + * 해당 옵션은 아래와 같습니다. | ||
4 | + * module, plugin, alias | ||
5 | + */ | ||
6 | +import { resolve } from 'path'; | ||
7 | + | ||
8 | +export default { | ||
9 | +// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins | ||
10 | + plugins: [ | ||
11 | + resolve(__dirname, '../src/plugins/ApiClient/index'), | ||
12 | + resolve(__dirname, '../src/plugins/antDesign'), | ||
13 | + resolve(__dirname, '../src/plugins/Dayjs/index'), | ||
14 | + { src: '@/plugins/client-only.client.js' }, | ||
15 | + { src: '@/plugins/globalMixins' }, | ||
16 | + { src: '@/plugins/vuelayers.js', ssr: false }, | ||
17 | + ], | ||
18 | + | ||
19 | + // Modules: https://go.nuxtjs.dev/config-modules | ||
20 | + modules: [ | ||
21 | + // https://go.nuxtjs.dev/axios | ||
22 | + '@nuxtjs/axios', | ||
23 | + '@nuxtjs/style-resources', | ||
24 | + '@nuxtjs/auth-next', | ||
25 | + '@nuxtjs/sitemap', | ||
26 | + '@nuxtjs/robots', | ||
27 | + 'nuxt-socket-io', | ||
28 | + 'nuxt-leaflet', | ||
29 | + resolve(__dirname, '../src/modules/vuelayers.js'), | ||
30 | + ], | ||
31 | + // alias | ||
32 | + alias: { | ||
33 | + '@': resolve(__dirname, '../src/'), | ||
34 | + images: resolve(__dirname, '../src/assets/images'), | ||
35 | + styles: resolve(__dirname, '../src/assets/styles'), | ||
36 | + }, | ||
37 | +}; |
Frontend/nuxtConfig/theme.js
0 → 100644
1 | +/** | ||
2 | + * 테마에 관련된 nuxt 옵션을 정리합니다. | ||
3 | + * 해당 옵션은 아래와 같습니다. | ||
4 | + * trainsition, css, loading | ||
5 | + */ | ||
6 | +import { resolve } from 'path'; | ||
7 | + | ||
8 | +export default { | ||
9 | +// Theme Animation | ||
10 | + loading: { | ||
11 | + color: '#1890ff', | ||
12 | + height: '4px', | ||
13 | + }, | ||
14 | + layoutTransition: { | ||
15 | + name: 'default-layout', | ||
16 | + mode: 'out-in', | ||
17 | + }, | ||
18 | + pageTransition: { | ||
19 | + name: 'default-page', | ||
20 | + mode: 'out-in', | ||
21 | + }, | ||
22 | + // Global CSS: https://go.nuxtjs.dev/config-css | ||
23 | + css: [ | ||
24 | + resolve(__dirname, '../src/assets/styles/less/index'), | ||
25 | + resolve(__dirname, '../src/assets/styles/scss/index'), | ||
26 | + ], | ||
27 | +}; |
Frontend/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "drone-web-nuxt", | ||
3 | + "version": "1.0.3", | ||
4 | + "private": true, | ||
5 | + "scripts": { | ||
6 | + "dev": "nuxt", | ||
7 | + "analyze-dev": "nuxt --analyze", | ||
8 | + "build": "nuxt build", | ||
9 | + "start": "nuxt start", | ||
10 | + "build-mo": "nuxt build --modern=server", | ||
11 | + "analyze-build": "nuxt build --analyze", | ||
12 | + "start-mo": "nuxt start --modern=server", | ||
13 | + "generate": "nuxt generate", | ||
14 | + "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .", | ||
15 | + "lint": "yarn lint:js", | ||
16 | + "serve": "json-server --watch lib/db.json --port 5555", | ||
17 | + "socket": "node ./serverMiddleWare/socket.js", | ||
18 | + "ws": "node ./serverMiddleWare/ws.js", | ||
19 | + "wsm": "node ./serverMiddleWare/multWs.js", | ||
20 | + "wsmp": "node ./serverMiddleWare/multPWs.js", | ||
21 | + "k6": "k6 run ./serverMiddleWare/k6.js", | ||
22 | + "k6m": "k6 run ./serverMiddleWare/k6mult.js" | ||
23 | + }, | ||
24 | + "dependencies": { | ||
25 | + "@nuxtjs/auth-next": "5.0.0-1613647907.37b1156", | ||
26 | + "@nuxtjs/axios": "^5.13.1", | ||
27 | + "@nuxtjs/robots": "^2.5.0", | ||
28 | + "@nuxtjs/sitemap": "^2.4.0", | ||
29 | + "ant-design-vue": "^1.7.2", | ||
30 | + "core-js": "^3.8.3", | ||
31 | + "cors": "^2.8.5", | ||
32 | + "dayjs": "^1.10.4", | ||
33 | + "express": "^4.17.1", | ||
34 | + "highcharts": "^9.0.1", | ||
35 | + "highcharts-vue": "^1.3.5", | ||
36 | + "nuxt": "^2.14.12", | ||
37 | + "nuxt-leaflet": "^0.0.25", | ||
38 | + "nuxt-socket-io": "^1.1.14", | ||
39 | + "socket.io": "^4.0.0", | ||
40 | + "store2": "^2.12.0", | ||
41 | + "vuelayers": "^0.11.35", | ||
42 | + "vuex": "^3.6.2", | ||
43 | + "ws": "^7.4.4" | ||
44 | + }, | ||
45 | + "devDependencies": { | ||
46 | + "@nuxt/image": "^0.4.13", | ||
47 | + "@nuxtjs/eslint-config": "^5.0.0", | ||
48 | + "@nuxtjs/eslint-module": "^3.0.2", | ||
49 | + "@nuxtjs/style-resources": "^1.0.0", | ||
50 | + "@vue/cli-service": "^4.5.11", | ||
51 | + "@vue/eslint-config-airbnb": "^5.3.0", | ||
52 | + "babel-eslint": "^10.1.0", | ||
53 | + "eslint": "^7.18.0", | ||
54 | + "eslint-import-resolver-alias": "^1.1.2", | ||
55 | + "eslint-plugin-nuxt": "^2.0.0", | ||
56 | + "eslint-plugin-vue": "^7.5.0", | ||
57 | + "fibers": "^5.0.0", | ||
58 | + "json-server": "^0.16.3", | ||
59 | + "less": "^4.1.1", | ||
60 | + "less-loader": "7", | ||
61 | + "sass": "^1.32.8", | ||
62 | + "sass-loader": "10" | ||
63 | + } | ||
64 | +} |
Frontend/serverMiddleWare/index.js
0 → 100644
1 | +// <project root>/api/index.js | ||
2 | +// const express = require('express'); | ||
3 | +const app = require('express')(); | ||
4 | +const http = require('http').createServer(app); | ||
5 | +const io = require('socket.io')(http); | ||
6 | + | ||
7 | +// 실제로는 /api 라우트를 처리하는 메소드가 된다. | ||
8 | +app.get('/', (req, res) => { | ||
9 | + console.log('hi'); | ||
10 | + io.emit('connection'); | ||
11 | + res.send('API root'); | ||
12 | +}); | ||
13 | + | ||
14 | +io.of('/analytics').on('connect', (socket) => { | ||
15 | + console.log('클라이언트 접속'); | ||
16 | + | ||
17 | + socket.on('disconnect', () => { | ||
18 | + console.log('클라이언트 접속 종료'); | ||
19 | + }); | ||
20 | + setInterval(() => { | ||
21 | + socket.emit('message', '메세지'); | ||
22 | + }, 3000); | ||
23 | +}); | ||
24 | + | ||
25 | +// 모듈로 사용할 수 있도록 export | ||
26 | +// 앱의 /api/* 라우트로 접근하는 모든 요청은 모두 app 인스턴스에게 전달된다. | ||
27 | +module.exports = { | ||
28 | + path: '/socket', | ||
29 | + handler: app, | ||
30 | +}; |
Frontend/serverMiddleWare/k6.js
0 → 100644
1 | +import ws from 'k6/ws'; | ||
2 | +import { check } from 'k6'; | ||
3 | + | ||
4 | +export const options = { | ||
5 | + // vus: 100, | ||
6 | + // duration: '30s', | ||
7 | + stages: [ | ||
8 | + { duration: '60s', target: 10 }, | ||
9 | + ], | ||
10 | +}; | ||
11 | + | ||
12 | +export default function () { | ||
13 | + const url = 'ws://localhost:8080'; | ||
14 | + const params = { tags: { my_tag: 'hello' } }; | ||
15 | + | ||
16 | + const res = ws.connect(url, params, (socket) => { | ||
17 | + socket.on('open', () => console.log('connected')); | ||
18 | + socket.on('message', (data) => console.log('Message received: ')); | ||
19 | + socket.on('close', () => console.log('disconnected')); | ||
20 | + socket.setTimeout(() => { | ||
21 | + console.log('60 seconds passed, closing the socket'); | ||
22 | + socket.close(); | ||
23 | + }, 30000); | ||
24 | + }); | ||
25 | + | ||
26 | + check(res, { 'status is 101': (r) => r && r.status === 101 }); | ||
27 | +} |
Frontend/serverMiddleWare/k6mult.js
0 → 100644
1 | +import ws from 'k6/ws'; | ||
2 | +import { check } from 'k6'; | ||
3 | + | ||
4 | +export const options = { | ||
5 | + // vus: 100, | ||
6 | + // duration: '30s', | ||
7 | + stages: [ | ||
8 | + { duration: '60s', target: 10 }, | ||
9 | + // { duration: '10s', target: 30 }, | ||
10 | + // { duration: '20s', target: 50 }, | ||
11 | + ], | ||
12 | +}; | ||
13 | + | ||
14 | +export default function () { | ||
15 | + const url = 'ws://localhost:8080'; | ||
16 | + const params = { tags: { my_tag: 'hello' } }; | ||
17 | + const res = {}; | ||
18 | + | ||
19 | + for (let i = 0; i < 10; i += 1) { | ||
20 | + res[i] = ws.connect(`${url}/${i + 1}`, params, (socket) => { | ||
21 | + socket.on('open', () => console.log('connected', i)); | ||
22 | + socket.on('message', (data) => console.log('Message received: ', i)); | ||
23 | + socket.on('close', () => console.log('disconnected')); | ||
24 | + socket.setTimeout(() => { | ||
25 | + console.log('60 seconds passed, closing the socket'); | ||
26 | + socket.close(); | ||
27 | + }, 60000); | ||
28 | + }); | ||
29 | + } | ||
30 | + console.log(res); | ||
31 | + check(res, { | ||
32 | + 'status1 is 101': (r) => r && r[0].status === 101, | ||
33 | + 'status2 is 101': (r) => r && r[1].status === 101, | ||
34 | + 'status3 is 101': (r) => r && r[2].status === 101, | ||
35 | + 'status4 is 101': (r) => r && r[3].status === 101, | ||
36 | + }); | ||
37 | +} | ||
38 | +// | ||
39 | +// const res1 = ws.connect(`${url}/${1}`, params, (socket) => { | ||
40 | +// socket.on('open', () => console.log('connected')); | ||
41 | +// socket.on('message', (data) => console.log('Message received: 1')); | ||
42 | +// socket.on('close', () => console.log('disconnected')); | ||
43 | +// socket.setTimeout(() => { | ||
44 | +// console.log('60 seconds passed, closing the socket'); | ||
45 | +// socket.close(); | ||
46 | +// }, 60000); | ||
47 | +// }); | ||
48 | +// | ||
49 | +// const res2 = ws.connect(`${url}/${2}`, params, (socket) => { | ||
50 | +// socket.on('open', () => console.log('connected')); | ||
51 | +// socket.on('message', (data) => console.log('Message received: 2')); | ||
52 | +// socket.on('close', () => console.log('disconnected')); | ||
53 | +// socket.setTimeout(() => { | ||
54 | +// console.log('60 seconds passed, closing the socket'); | ||
55 | +// socket.close(); | ||
56 | +// }, 60000); | ||
57 | +// }); | ||
58 | +// | ||
59 | +// const res3 = ws.connect(`${url}/${3}`, params, (socket) => { | ||
60 | +// socket.on('open', () => console.log('connected')); | ||
61 | +// socket.on('message', (data) => console.log('Message received: 3')); | ||
62 | +// socket.on('close', () => console.log('disconnected')); | ||
63 | +// socket.setTimeout(() => { | ||
64 | +// console.log('60 seconds passed, closing the socket'); | ||
65 | +// socket.close(); | ||
66 | +// }, 60000); | ||
67 | +// }); | ||
68 | +// | ||
69 | +// const res4 = ws.connect(`${url}/${4}`, params, (socket) => { | ||
70 | +// socket.on('open', () => console.log('connected')); | ||
71 | +// socket.on('message', (data) => console.log('Message received: 4')); | ||
72 | +// socket.on('close', () => console.log('disconnected')); | ||
73 | +// socket.setTimeout(() => { | ||
74 | +// console.log('60 seconds passed, closing the socket'); | ||
75 | +// socket.close(); | ||
76 | +// }, 60000); | ||
77 | +// }); | ||
78 | +// | ||
79 | +// const res5 = ws.connect(`${url}/${5}`, params, (socket) => { | ||
80 | +// socket.on('open', () => console.log('connected')); | ||
81 | +// socket.on('message', (data) => console.log('Message received: 5')); | ||
82 | +// socket.on('close', () => console.log('disconnected')); | ||
83 | +// socket.setTimeout(() => { | ||
84 | +// console.log('60 seconds passed, closing the socket'); | ||
85 | +// socket.close(); | ||
86 | +// }, 60000); | ||
87 | +// }); | ||
88 | +// | ||
89 | +// const res6 = ws.connect(`${url}/${6}`, params, (socket) => { | ||
90 | +// socket.on('open', () => console.log('connected')); | ||
91 | +// socket.on('message', (data) => console.log('Message received: 6')); | ||
92 | +// socket.on('close', () => console.log('disconnected')); | ||
93 | +// socket.setTimeout(() => { | ||
94 | +// console.log('60 seconds passed, closing the socket'); | ||
95 | +// socket.close(); | ||
96 | +// }, 60000); | ||
97 | +// }); | ||
98 | +// | ||
99 | +// const res7 = ws.connect(`${url}/${7}`, params, (socket) => { | ||
100 | +// socket.on('open', () => console.log('connected')); | ||
101 | +// socket.on('message', (data) => console.log('Message received: 7')); | ||
102 | +// socket.on('close', () => console.log('disconnected')); | ||
103 | +// socket.setTimeout(() => { | ||
104 | +// console.log('60 seconds passed, closing the socket'); | ||
105 | +// socket.close(); | ||
106 | +// }, 60000); | ||
107 | +// }); | ||
108 | +// | ||
109 | +// const res8 = ws.connect(`${url}/${8}`, params, (socket) => { | ||
110 | +// socket.on('open', () => console.log('connected')); | ||
111 | +// socket.on('message', (data) => console.log('Message received: 8')); | ||
112 | +// socket.on('close', () => console.log('disconnected')); | ||
113 | +// socket.setTimeout(() => { | ||
114 | +// console.log('60 seconds passed, closing the socket'); | ||
115 | +// socket.close(); | ||
116 | +// }, 60000); | ||
117 | +// }); | ||
118 | +// | ||
119 | +// const res9 = ws.connect(`${url}/${9}`, params, (socket) => { | ||
120 | +// socket.on('open', () => console.log('connected')); | ||
121 | +// socket.on('message', (data) => console.log('Message received: 9')); | ||
122 | +// socket.on('close', () => console.log('disconnected')); | ||
123 | +// socket.setTimeout(() => { | ||
124 | +// console.log('60 seconds passed, closing the socket'); | ||
125 | +// socket.close(); | ||
126 | +// }, 60000); | ||
127 | +// }); | ||
128 | +// | ||
129 | +// const res10 = ws.connect(`${url}/${10}`, params, (socket) => { | ||
130 | +// socket.on('open', () => console.log('connected')); | ||
131 | +// socket.on('message', (data) => console.log('Message received: 10')); | ||
132 | +// socket.on('close', () => console.log('disconnected')); | ||
133 | +// socket.setTimeout(() => { | ||
134 | +// console.log('60 seconds passed, closing the socket'); | ||
135 | +// socket.close(); | ||
136 | +// }, 60000); | ||
137 | +// }); | ||
138 | +// check([res1, res2, res3, res10], { 'status is 101': (r) => r && r.status === 101 }); |
Frontend/serverMiddleWare/multPWs.js
0 → 100644
1 | +/* eslint-disable prefer-arrow-callback,consistent-return,no-param-reassign,no-mixed-operators,no-use-before-define */ | ||
2 | +const http = require('http'); | ||
3 | +const WebSocket = require('ws'); | ||
4 | +const url = require('url'); | ||
5 | + | ||
6 | +const server = http.createServer(); | ||
7 | +const wss = {}; | ||
8 | + | ||
9 | +const dataNum = process.argv[2]; | ||
10 | +const wsServerCnt = process.argv[3]; | ||
11 | +const port = process.argv[4]; | ||
12 | +const dataPerWsServer = dataNum / wsServerCnt; | ||
13 | + | ||
14 | +console.log(process.argv); | ||
15 | + | ||
16 | +let pingInterval = null; | ||
17 | +let sendInterval = null; | ||
18 | + | ||
19 | +const logData = []; | ||
20 | +for (let i = 0; i < dataPerWsServer; i += 1) { | ||
21 | + logData.push({ | ||
22 | + latitude: getRandomArbitrary(37200000000000, 37300000000000) / 1000000000000, | ||
23 | + longitude: getRandomArbitrary(126900000000000, 127100000000000) / 1000000000000, | ||
24 | + id: i, | ||
25 | + }); | ||
26 | +} | ||
27 | + | ||
28 | +function getRandomArbitrary(min, max) { | ||
29 | + return parseInt((Math.random() * (max - min) + min), 10); | ||
30 | +} | ||
31 | +function circleMove(x, y, radius, max, circleStep) { | ||
32 | + return { | ||
33 | + latitude: x + radius * Math.cos(2 * Math.PI * circleStep / max), | ||
34 | + longitude: y + radius * Math.sin(2 * Math.PI * circleStep / max), | ||
35 | + }; | ||
36 | +} | ||
37 | +function makeCoordData(log, circleStep) { | ||
38 | + if (circleStep == null) { | ||
39 | + circleStep = 0; | ||
40 | + } | ||
41 | + if (circleStep === 3600) { | ||
42 | + circleStep = 0; | ||
43 | + } else circleStep += 1; | ||
44 | + | ||
45 | + // console.log('step', circleStep); | ||
46 | + | ||
47 | + return Array.from( | ||
48 | + { length: dataPerWsServer }, | ||
49 | + (v, i) => ({ | ||
50 | + id: i, | ||
51 | + ...circleMove(log[i].latitude, log[i].longitude, 0.05, 3600, circleStep), | ||
52 | + time: new Date(), | ||
53 | + }), | ||
54 | + ); | ||
55 | +} | ||
56 | +function heartbeat() { | ||
57 | + this.isAlive = true; | ||
58 | + console.log('client Heartbeat'); | ||
59 | +} | ||
60 | +function noop() {} | ||
61 | + | ||
62 | +for (let i = 0; i < wsServerCnt; i += 1) { | ||
63 | + wss[i + 1] = new WebSocket.Server({ noServer: true }); | ||
64 | +} | ||
65 | + | ||
66 | +Object.entries(wss).forEach(([key, wss]) => { | ||
67 | + wss.on('connection', (ws) => { | ||
68 | + console.log('connected', key); | ||
69 | + ws.isAlive = true; | ||
70 | + ws.on('pong', heartbeat); | ||
71 | + ws.on('message', function incoming(message) { | ||
72 | + console.log('received: %s', message); | ||
73 | + }); | ||
74 | + let circleStep = 0; | ||
75 | + sendInterval = setInterval(() => { | ||
76 | + if (ws.readyState === WebSocket.OPEN) { | ||
77 | + const coordData = makeCoordData(logData, circleStep); | ||
78 | + ws.send(JSON.stringify(coordData)); | ||
79 | + } | ||
80 | + circleStep += 1; | ||
81 | + }, 1000); | ||
82 | + | ||
83 | + ws.on('close', function close() { | ||
84 | + console.log('websocket Closed'); | ||
85 | + clearInterval(pingInterval); | ||
86 | + clearInterval(sendInterval); | ||
87 | + // sendInterval = null; | ||
88 | + }); | ||
89 | + | ||
90 | + /* ping check */ | ||
91 | + pingInterval = setInterval(function ping() { | ||
92 | + wss.clients.forEach(function each(ws) { | ||
93 | + if (ws.isAlive === false) return ws.terminate(); | ||
94 | + ws.isAlive = false; | ||
95 | + ws.ping(noop); | ||
96 | + }); | ||
97 | + }, 30000); | ||
98 | + | ||
99 | + wss.on('close', function close() { | ||
100 | + console.log('server closed'); | ||
101 | + clearInterval(pingInterval); | ||
102 | + clearInterval(sendInterval); | ||
103 | + }); | ||
104 | + }); | ||
105 | +}); | ||
106 | + | ||
107 | +server.on('upgrade', (request, socket, head) => { | ||
108 | + const { pathname } = url.parse(request.url); | ||
109 | + | ||
110 | + Object.entries(wss).forEach(([key, wss]) => { | ||
111 | + if (`/${key}` === pathname) { | ||
112 | + wss.handleUpgrade(request, socket, head, (ws) => { | ||
113 | + wss.emit('connection', ws, request); | ||
114 | + }); | ||
115 | + } | ||
116 | + }); | ||
117 | +}); | ||
118 | + | ||
119 | +server.listen(port); |
Frontend/serverMiddleWare/multWs.js
0 → 100644
1 | +/* eslint-disable prefer-arrow-callback,consistent-return,no-param-reassign,no-mixed-operators,no-use-before-define */ | ||
2 | +const http = require('http'); | ||
3 | +const WebSocket = require('ws'); | ||
4 | +const url = require('url'); | ||
5 | + | ||
6 | +const server = http.createServer(); | ||
7 | +const wss = {}; | ||
8 | + | ||
9 | +const dataNum = 20000; | ||
10 | +const wsServerCnt = process.argv[2]; | ||
11 | +console.log(wsServerCnt); | ||
12 | +const dataPerWsServer = dataNum / wsServerCnt; | ||
13 | + | ||
14 | +let pingInterval = null; | ||
15 | +let sendInterval = null; | ||
16 | + | ||
17 | +const logData = []; | ||
18 | +for (let i = 0; i < dataPerWsServer; i += 1) { | ||
19 | + logData.push({ | ||
20 | + latitude: getRandomArbitrary(37200000000000, 37300000000000) / 1000000000000, | ||
21 | + longitude: getRandomArbitrary(126900000000000, 127100000000000) / 1000000000000, | ||
22 | + id: i, | ||
23 | + }); | ||
24 | +} | ||
25 | + | ||
26 | +function getRandomArbitrary(min, max) { | ||
27 | + return parseInt((Math.random() * (max - min) + min), 10); | ||
28 | +} | ||
29 | +function circleMove(x, y, radius, max, circleStep) { | ||
30 | + return { | ||
31 | + latitude: x + radius * Math.cos(2 * Math.PI * circleStep / max), | ||
32 | + longitude: y + radius * Math.sin(2 * Math.PI * circleStep / max), | ||
33 | + }; | ||
34 | +} | ||
35 | +function makeCoordData(log, circleStep) { | ||
36 | + if (circleStep == null) { | ||
37 | + circleStep = 0; | ||
38 | + } | ||
39 | + if (circleStep === 3600) { | ||
40 | + circleStep = 0; | ||
41 | + } else circleStep += 1; | ||
42 | + | ||
43 | + // console.log('step', circleStep); | ||
44 | + | ||
45 | + return Array.from( | ||
46 | + { length: dataPerWsServer }, | ||
47 | + (v, i) => ({ | ||
48 | + id: i, | ||
49 | + ...circleMove(log[i].latitude, log[i].longitude, 0.05, 3600, circleStep), | ||
50 | + time: new Date(), | ||
51 | + }), | ||
52 | + ); | ||
53 | +} | ||
54 | +function heartbeat() { | ||
55 | + this.isAlive = true; | ||
56 | + console.log('client Heartbeat'); | ||
57 | +} | ||
58 | +function noop() {} | ||
59 | + | ||
60 | +for (let i = 0; i < wsServerCnt; i += 1) { | ||
61 | + wss[i + 1] = new WebSocket.Server({ noServer: true }); | ||
62 | +} | ||
63 | + | ||
64 | +Object.entries(wss).forEach(([key, wss]) => { | ||
65 | + wss.on('connection', (ws) => { | ||
66 | + console.log('connected', key); | ||
67 | + ws.isAlive = true; | ||
68 | + ws.on('pong', heartbeat); | ||
69 | + ws.on('message', function incoming(message) { | ||
70 | + console.log('received: %s', message); | ||
71 | + }); | ||
72 | + let circleStep = 0; | ||
73 | + sendInterval = setInterval(() => { | ||
74 | + if (ws.readyState === WebSocket.OPEN) { | ||
75 | + const coordData = makeCoordData(logData, circleStep); | ||
76 | + ws.send(JSON.stringify(coordData)); | ||
77 | + } | ||
78 | + circleStep += 1; | ||
79 | + }, 1000); | ||
80 | + | ||
81 | + ws.on('close', function close() { | ||
82 | + console.log('websocket Closed'); | ||
83 | + clearInterval(pingInterval); | ||
84 | + clearInterval(sendInterval); | ||
85 | + // sendInterval = null; | ||
86 | + }); | ||
87 | + | ||
88 | + /* ping check */ | ||
89 | + pingInterval = setInterval(function ping() { | ||
90 | + wss.clients.forEach(function each(ws) { | ||
91 | + if (ws.isAlive === false) return ws.terminate(); | ||
92 | + ws.isAlive = false; | ||
93 | + ws.ping(noop); | ||
94 | + }); | ||
95 | + }, 30000); | ||
96 | + | ||
97 | + wss.on('close', function close() { | ||
98 | + console.log('server closed'); | ||
99 | + clearInterval(pingInterval); | ||
100 | + clearInterval(sendInterval); | ||
101 | + }); | ||
102 | + }); | ||
103 | +}); | ||
104 | + | ||
105 | +server.on('upgrade', (request, socket, head) => { | ||
106 | + const { pathname } = url.parse(request.url); | ||
107 | + | ||
108 | + Object.entries(wss).forEach(([key, wss]) => { | ||
109 | + if (`/${key}` === pathname) { | ||
110 | + wss.handleUpgrade(request, socket, head, (ws) => { | ||
111 | + wss.emit('connection', ws, request); | ||
112 | + }); | ||
113 | + } | ||
114 | + }); | ||
115 | +}); | ||
116 | + | ||
117 | +server.listen(20203); |
Frontend/serverMiddleWare/socket.js
0 → 100644
1 | +/* eslint-disable prefer-arrow-callback */ | ||
2 | +// <project root>/api/index.js | ||
3 | +// const express = require('express'); | ||
4 | + | ||
5 | +function getRandomArbitrary(min, max) { | ||
6 | + return parseInt((Math.random() * (max - min) + min), 10); | ||
7 | +} | ||
8 | +const app = require('express')(); | ||
9 | +const http = require('http').createServer(app); | ||
10 | +const io = require('socket.io')(http, { | ||
11 | + cors: true, | ||
12 | + origins: ['http://127.0.0.1:3000', 'http://127.0.0.1:8888', 'http://localhost:3000'], | ||
13 | +}); | ||
14 | +const cors = require('cors'); | ||
15 | + | ||
16 | +app.use(cors()); | ||
17 | + | ||
18 | +// 실제로는 /api 라우트를 처리하는 메소드가 된다. | ||
19 | +app.get('/', (req, res) => { | ||
20 | + console.log('hi'); | ||
21 | + io.of('/testSoc').emit('connection', { data: '1234' }); | ||
22 | + res.send('API root'); | ||
23 | +}); | ||
24 | + | ||
25 | +io.of('/testSoc').on('connect', (socket) => { | ||
26 | + console.log('클라이언트 접속'); | ||
27 | + | ||
28 | + socket.on('getMessage', (data) => { | ||
29 | + console.log('fromClient', data); | ||
30 | + }); | ||
31 | + socket.on('disconnect', () => { | ||
32 | + console.log('클라이언트 접속 종료'); | ||
33 | + }); | ||
34 | + setInterval(() => { | ||
35 | + socket.emit('receiveLog', { num: getRandomArbitrary(10, 100), time: new Date() }); | ||
36 | + }, 1000); | ||
37 | +}); | ||
38 | + | ||
39 | +http.listen(8888, () => { | ||
40 | + console.log('Socket IO server listening on port 8888'); | ||
41 | +}); |
Frontend/serverMiddleWare/test.js
0 → 100644
1 | +import ws from 'k6/ws'; | ||
2 | +import { check } from 'k6'; | ||
3 | + | ||
4 | +export default function () { | ||
5 | + const url = 'ws://echo.websocket.org'; | ||
6 | + const params = { tags: { my_tag: 'hello' } }; | ||
7 | + | ||
8 | + const res = ws.connect(url, params, (socket) => { | ||
9 | + socket.on('open', () => { | ||
10 | + console.log('connected'); | ||
11 | + | ||
12 | + socket.setInterval(() => { | ||
13 | + socket.ping(); | ||
14 | + console.log('Pinging every 1sec (setInterval test)'); | ||
15 | + }, 1000); | ||
16 | + }); | ||
17 | + | ||
18 | + socket.on('ping', () => { | ||
19 | + console.log('PING!'); | ||
20 | + }); | ||
21 | + | ||
22 | + socket.on('pong', () => { | ||
23 | + console.log('PONG!'); | ||
24 | + }); | ||
25 | + | ||
26 | + socket.on('close', () => { | ||
27 | + console.log('disconnected'); | ||
28 | + }); | ||
29 | + | ||
30 | + socket.setTimeout(() => { | ||
31 | + console.log('2 seconds passed, closing the socket'); | ||
32 | + socket.close(); | ||
33 | + }, 2000); | ||
34 | + }); | ||
35 | + | ||
36 | + check(res, { | ||
37 | + 'status is 101': (r) => r && r.status === 101, | ||
38 | + 'Homepage body size is 11026 bytes': (r) => r.body && r.body.length === 11026, | ||
39 | + test: (r) => r, | ||
40 | + }); | ||
41 | +} |
Frontend/serverMiddleWare/ws.js
0 → 100644
1 | +/* eslint-disable prefer-arrow-callback,consistent-return,no-param-reassign,no-mixed-operators,no-use-before-define */ | ||
2 | +let pingInterval = null; | ||
3 | +let sendInterval = null; | ||
4 | +const logData = []; | ||
5 | +const dataNum = 20000; | ||
6 | +for (let i = 0; i < dataNum; i += 1) { | ||
7 | + logData.push({ | ||
8 | + latitude: getRandomArbitrary(37200000000000, 37300000000000) / 1000000000000, | ||
9 | + longitude: getRandomArbitrary(126900000000000, 127100000000000) / 1000000000000, | ||
10 | + id: i, | ||
11 | + }); | ||
12 | +} | ||
13 | + | ||
14 | +function getRandomArbitrary(min, max) { | ||
15 | + return parseInt((Math.random() * (max - min) + min), 10); | ||
16 | +} | ||
17 | +function circleMove(x, y, radius, max, circleStep) { | ||
18 | + return { | ||
19 | + latitude: x + radius * Math.cos(2 * Math.PI * circleStep / max), | ||
20 | + longitude: y + radius * Math.sin(2 * Math.PI * circleStep / max), | ||
21 | + }; | ||
22 | +} | ||
23 | +function makeCoordData(log, circleStep) { | ||
24 | + if (circleStep == null) { | ||
25 | + circleStep = 0; | ||
26 | + } | ||
27 | + if (circleStep === 3600) { | ||
28 | + circleStep = 0; | ||
29 | + } else circleStep += 1; | ||
30 | + | ||
31 | + console.log('step', circleStep); | ||
32 | + | ||
33 | + return Array.from( | ||
34 | + { length: dataNum }, | ||
35 | + (v, i) => ({ | ||
36 | + id: i, | ||
37 | + ...circleMove(log[i].latitude, log[i].longitude, 0.05, 3600, circleStep), | ||
38 | + time: new Date(), | ||
39 | + }), | ||
40 | + ); | ||
41 | +} | ||
42 | +function heartbeat() { | ||
43 | + this.isAlive = true; | ||
44 | + console.log('client Heartbeat'); | ||
45 | +} | ||
46 | +function noop() {} | ||
47 | + | ||
48 | +const WebSocket = require('ws'); | ||
49 | + | ||
50 | +const wss = new WebSocket.Server({ | ||
51 | + port: 20202, | ||
52 | + perMessageDeflate: { | ||
53 | + zlibDeflateOptions: { | ||
54 | + // See zlib defaults. | ||
55 | + chunkSize: 1024, | ||
56 | + memLevel: 7, | ||
57 | + level: 3, | ||
58 | + }, | ||
59 | + zlibInflateOptions: { | ||
60 | + chunkSize: 10 * 1024, | ||
61 | + }, | ||
62 | + // Other options settable: | ||
63 | + clientNoContextTakeover: true, // Defaults to negotiated value. | ||
64 | + serverNoContextTakeover: true, // Defaults to negotiated value. | ||
65 | + serverMaxWindowBits: 10, // Defaults to negotiated value. | ||
66 | + // Below options specified as default values. | ||
67 | + concurrencyLimit: 10, // Limits zlib concurrency for perf. | ||
68 | + threshold: 1024, // Size (in bytes) below which messages | ||
69 | + // should not be compressed. | ||
70 | + }, | ||
71 | +}); | ||
72 | + | ||
73 | +wss.on('connection', function connection(ws) { | ||
74 | + ws.isAlive = true; | ||
75 | + ws.on('pong', heartbeat); | ||
76 | + ws.on('message', function incoming(message) { | ||
77 | + console.log('received: %s', message); | ||
78 | + }); | ||
79 | + let circleStep = 0; | ||
80 | + sendInterval = setInterval(() => { | ||
81 | + if (ws.readyState === WebSocket.OPEN) { | ||
82 | + const coordData = makeCoordData(logData, circleStep); | ||
83 | + ws.send(JSON.stringify(coordData)); | ||
84 | + } | ||
85 | + circleStep += 1; | ||
86 | + }, 1000); | ||
87 | + | ||
88 | + ws.on('close', function close() { | ||
89 | + console.log('websocket Closed'); | ||
90 | + clearInterval(pingInterval); | ||
91 | + clearInterval(sendInterval); | ||
92 | + // sendInterval = null; | ||
93 | + }); | ||
94 | +}); | ||
95 | + | ||
96 | +/* ping check */ | ||
97 | +pingInterval = setInterval(function ping() { | ||
98 | + wss.clients.forEach(function each(ws) { | ||
99 | + if (ws.isAlive === false) return ws.terminate(); | ||
100 | + ws.isAlive = false; | ||
101 | + ws.ping(noop); | ||
102 | + }); | ||
103 | +}, 30000); | ||
104 | + | ||
105 | +wss.on('close', function close() { | ||
106 | + console.log('server closed'); | ||
107 | + clearInterval(pingInterval); | ||
108 | + clearInterval(sendInterval); | ||
109 | +}); |
Frontend/src/assets/README.md
0 → 100644
1 | +# ASSETS | ||
2 | + | ||
3 | +**This directory is not required, you can delete it if you don't want to use it.** | ||
4 | + | ||
5 | +This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. | ||
6 | + | ||
7 | +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). |
Frontend/src/assets/images/54763744_p0.png
0 → 100755
2.55 MB
Frontend/src/assets/images/drone-image.jpg
0 → 100644
25.5 KB
Frontend/src/assets/images/drone.png
0 → 100644
80.2 KB
Frontend/src/assets/styles/less/index.less
0 → 100644
1 | +/* default Variable */ | ||
2 | +@primary-color: #1890ff; // primary color for all components | ||
3 | +@link-color: #1890ff; // link color | ||
4 | +@success-color: #52c41a; // success state color | ||
5 | +@warning-color: #faad14; // warning state color | ||
6 | +@error-color: #f5222d; // error state color | ||
7 | +@font-size-base: 14px; // major text font size | ||
8 | +@heading-color: rgba(0, 0, 0, 0.85); // heading text color | ||
9 | +@text-color: rgba(0, 0, 0, 0.65); // major text color | ||
10 | +@text-color-secondary: rgba(0, 0, 0, 0.45); // secondary text color | ||
11 | +@disabled-color: rgba(0, 0, 0, 0.25); // disable state color | ||
12 | +@border-radius-base: 4px; // major border radius | ||
13 | +@border-color-base: #d9d9d9; // major border color | ||
14 | +@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // major shadow for layers |
Frontend/src/assets/styles/mixins.scss
0 → 100644
1 | +// Colors | ||
2 | +$white: #fff; | ||
3 | +$black: #001529; | ||
4 | +$blue: var(--kit-color-primary); | ||
5 | +$blue-light: #3d6ee7; | ||
6 | +$blue-dark: #103daf; | ||
7 | +$gray-1: #f2f4f8; | ||
8 | +$gray-2: #e4e9f0; | ||
9 | +$gray-3: #dde2ec; | ||
10 | +$gray-4: #c3bedc; | ||
11 | +$gray-5: #aca6cc; | ||
12 | +$gray-6: #786fa4; | ||
13 | +$yellow: #ff0; | ||
14 | +$orange: #f2a654; | ||
15 | +$red: #b52427; | ||
16 | +$pink: #fd3995; | ||
17 | +$purple: #652eff; | ||
18 | +$green: #41b883; | ||
19 | +$kdis-color: #0c7037; | ||
20 | +$antblue: #1890ff; | ||
21 | + | ||
22 | +$text: $gray-6; | ||
23 | +$border: $gray-2; | ||
24 | + | ||
25 | +// Accent colors | ||
26 | +$default: $gray-4; | ||
27 | +$primary: $kdis-color; | ||
28 | +$secondary: $gray-5; | ||
29 | +$success: $green; | ||
30 | +$info: $blue-light; | ||
31 | +$warning: $orange; | ||
32 | +$danger: $red; | ||
33 | +$light: $gray-1; | ||
34 | +$dark: $black; | ||
35 | + | ||
36 | +// dark theme | ||
37 | +$dark-gray-1: #aeaee0; | ||
38 | +$dark-gray-2: #7575a3; | ||
39 | +$dark-gray-3: #4f4f7a; | ||
40 | +$dark-gray-4: #2a274d; | ||
41 | +$dark-gray-5: #161537; | ||
42 | +$dark-gray-6: #100f28; | ||
43 | + | ||
44 | +// Font Family | ||
45 | +$base-font-family: 'Noto Sans KR', sans-serif; | ||
46 | + | ||
47 | +// Font Size | ||
48 | +$base-font-size: 15 !default; |
Frontend/src/assets/styles/scss/index.scss
0 → 100644
1 | +@import '@/assets/styles/scss/partials/transition.scss'; | ||
2 | +@import '@/assets/styles/scss/partials/page.scss'; | ||
3 | +@import '@/assets/styles/scss/partials/layouts.scss'; | ||
4 | +@import '@/assets/styles/scss/partials/description.scss'; | ||
5 | +@import '@/assets/styles/scss/partials/box.scss'; | ||
6 | +@import '@/assets/styles/scss/partials/test.scss'; | ||
7 | +@import '@/assets/styles/scss/partials/pagination.scss'; | ||
8 | +@import '@/assets/styles/scss/partials/alert.scss'; |
1 | + | ||
2 | +.mapBox { | ||
3 | + ::-webkit-scrollbar { | ||
4 | + width: 7px; | ||
5 | + border-radius: 10px; | ||
6 | + } | ||
7 | + | ||
8 | + ::-webkit-scrollbar-thumb { | ||
9 | + background-clip: padding-box; | ||
10 | + background-color: #47749E; | ||
11 | + border: 2px solid transparent; | ||
12 | + width: 5px; | ||
13 | + border-radius: 2px; | ||
14 | + } | ||
15 | + ::-webkit-scrollbar-track { | ||
16 | + background-color: rgb(236, 236, 236); | ||
17 | + border-radius: 0px 2px 2px 0px; | ||
18 | + } | ||
19 | + | ||
20 | + | ||
21 | + .search-box { | ||
22 | + position: absolute; | ||
23 | + right: 10px; | ||
24 | + top: 10px; | ||
25 | + | ||
26 | + .searchBtn{ | ||
27 | + width: 50px; | ||
28 | + height: 50px; | ||
29 | + padding: 7px; | ||
30 | + background: white; | ||
31 | + border: 2px solid rgba(0,0,0,0.2); | ||
32 | + border-radius: 4px; | ||
33 | + cursor: pointer; | ||
34 | + } | ||
35 | + | ||
36 | + .ant-modal-content { | ||
37 | + position: absolute; | ||
38 | + right: 60px; | ||
39 | + top: 0px; | ||
40 | + width: 400px; | ||
41 | + max-height: calc(100vh - 120px); | ||
42 | + | ||
43 | + .ant-modal-header { | ||
44 | + background: #47749e; | ||
45 | + .ant-modal-title { | ||
46 | + color: white; | ||
47 | + text-align: center; | ||
48 | + } | ||
49 | + } | ||
50 | + | ||
51 | + .ant-modal-body { | ||
52 | + padding: 20px; | ||
53 | + } | ||
54 | + | ||
55 | + .ant-input:hover { | ||
56 | + border-color: #47749e; | ||
57 | + border-right-width: 1px !important; | ||
58 | + } | ||
59 | + | ||
60 | + .ant-input:focus { | ||
61 | + border-color: #47749e; | ||
62 | + } | ||
63 | + | ||
64 | + .ant-btn-primary { | ||
65 | + background-color: #47749e; | ||
66 | + border-color: #47749e; | ||
67 | + } | ||
68 | + | ||
69 | + .ant-list-header { | ||
70 | + color: white; | ||
71 | + background: #47749e; | ||
72 | + } | ||
73 | + | ||
74 | + .ant-input-search { | ||
75 | + width: 100%; | ||
76 | + margin-bottom: 10px; | ||
77 | + } | ||
78 | + | ||
79 | + .ant-list-items { | ||
80 | + max-height: calc(100vh - 300px); | ||
81 | + overflow-y: scroll; | ||
82 | + } | ||
83 | + | ||
84 | + .ant-list-item { | ||
85 | + padding: 10px; | ||
86 | + background: white; | ||
87 | + width: 100%; | ||
88 | + } | ||
89 | + | ||
90 | + .ant-list-item:hover { | ||
91 | + color: #47749e; | ||
92 | + font-weight: 600; | ||
93 | + } | ||
94 | + } | ||
95 | + } | ||
96 | +} | ||
97 | +.filter-feature-box { | ||
98 | + .ant-modal-content { | ||
99 | + position: absolute; | ||
100 | + right: 70px; | ||
101 | + top: 10px; | ||
102 | + width: 300px; | ||
103 | + | ||
104 | + .ant-modal-header { | ||
105 | + background: #47749e; | ||
106 | + .ant-modal-title { | ||
107 | + color: white; | ||
108 | + text-align: center; | ||
109 | + } | ||
110 | + } | ||
111 | + } | ||
112 | + | ||
113 | + .label { | ||
114 | + font-size: 13px; | ||
115 | + font-weight: 700; | ||
116 | + margin-bottom: 10px; | ||
117 | + margin-top: 20px; | ||
118 | + | ||
119 | + span { | ||
120 | + display: inline-block; | ||
121 | + border: 2px solid #47749e; | ||
122 | + border-radius: 3px; | ||
123 | + padding: 2px 5px; | ||
124 | + } | ||
125 | + | ||
126 | + } | ||
127 | +} | ||
128 | + | ||
129 | +.boxBtn { | ||
130 | + display: flex; | ||
131 | + justify-content: center; | ||
132 | + align-items: center; | ||
133 | + width: 50px; | ||
134 | + height: 50px; | ||
135 | + padding: 7px; | ||
136 | + background: white; | ||
137 | + border: 2px solid rgba(0,0,0,0.2); | ||
138 | + border-radius: 4px; | ||
139 | + cursor: pointer; | ||
140 | + font-size: 30px; | ||
141 | +} | ||
142 | + | ||
143 | +.bottom-tool-box { | ||
144 | + display: flex; | ||
145 | + gap: 10px; | ||
146 | + position: absolute; | ||
147 | + bottom: 10px; | ||
148 | + right: calc(50% - 25px); | ||
149 | + .filterBox { | ||
150 | + .filterBtn { | ||
151 | + width: 50px; | ||
152 | + height: 50px; | ||
153 | + padding: 7px; | ||
154 | + background: white; | ||
155 | + border: 2px solid rgba(0,0,0,0.2); | ||
156 | + border-radius: 4px; | ||
157 | + cursor: pointer; | ||
158 | + } | ||
159 | + } | ||
160 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500&family=Roboto:wght@500&display=swap'); | ||
2 | + | ||
3 | +.ant-descriptions-bordered .ant-descriptions-item-label { | ||
4 | + background-color: #47749e; | ||
5 | + color: white; | ||
6 | +} | ||
7 | + | ||
8 | +.ant-descriptions-item { | ||
9 | + display: inline-flex; | ||
10 | + margin-right: 10px; | ||
11 | +} | ||
12 | + | ||
13 | +.ant-descriptions-row { | ||
14 | + font-family: 'Noto Sans KR', sans-serif; | ||
15 | + .ant-descriptions-item-content { | ||
16 | + min-width: 150px; | ||
17 | + font-size: 13px; | ||
18 | + background: white; | ||
19 | + } | ||
20 | +} | ||
21 | + | ||
22 | +.ant-descriptions-item > span { | ||
23 | + align-self: center; | ||
24 | +} | ||
25 | + | ||
26 | +.ant-descriptions-bordered .ant-descriptions-item-label, | ||
27 | +.ant-descriptions-bordered .ant-descriptions-item-content { | ||
28 | + padding: 10px 10px; | ||
29 | +} | ||
30 | +.description-box .ant-descriptions-item { | ||
31 | + display: table-cell; | ||
32 | +} | ||
33 | + | ||
34 | +.drone-detail { | ||
35 | + .ant-descriptions-view { | ||
36 | + max-height: calc(100vh - 120px); | ||
37 | + overflow: auto; | ||
38 | + } | ||
39 | + | ||
40 | + .ant-descriptions-item-label.ant-descriptions-item-colon { | ||
41 | + font-size: 16px; | ||
42 | + } | ||
43 | +} | ||
44 | + | ||
45 | +.ant-page-header-content { | ||
46 | + .search-box .ant-descriptions-row .ant-descriptions-item-content { | ||
47 | + min-width: 0; | ||
48 | + } | ||
49 | + .search-box .ant-descriptions-row { | ||
50 | + font-family: none; | ||
51 | + } | ||
52 | +} |
1 | + | ||
2 | +.mt-size-default{ | ||
3 | + margin-top: 8px; | ||
4 | +} | ||
5 | +.mt-size-sm { | ||
6 | + margin-top: 4px; | ||
7 | +} | ||
8 | +.mt-size-md { | ||
9 | + margin-top: 12px; | ||
10 | +} | ||
11 | +.mt-size-lg { | ||
12 | + margin-top: 20px; | ||
13 | +} | ||
14 | + | ||
15 | +.ml-size-default{ | ||
16 | + margin-left: 8px; | ||
17 | +} | ||
18 | +.ml-size-sm { | ||
19 | + margin-left: 4px; | ||
20 | +} | ||
21 | +.ml-size-md { | ||
22 | + margin-left: 12px; | ||
23 | +} | ||
24 | +.ml-size-lg { | ||
25 | + margin-left: 20px; | ||
26 | +} | ||
27 | + | ||
28 | +.mr-size-default{ | ||
29 | + margin-right: 8px; | ||
30 | +} | ||
31 | +.mr-size-sm { | ||
32 | + margin-right: 4px; | ||
33 | +} | ||
34 | +.mr-size-md { | ||
35 | + margin-right: 12px; | ||
36 | +} | ||
37 | +.mr-size-lg { | ||
38 | + margin-right: 20px; | ||
39 | +} | ||
40 | + | ||
41 | +.mb-size-default{ | ||
42 | + margin-bottom: 8px; | ||
43 | +} | ||
44 | +.mb-size-sm { | ||
45 | + margin-bottom: 4px; | ||
46 | +} | ||
47 | +.mb-size-md { | ||
48 | + margin-bottom: 12px; | ||
49 | +} | ||
50 | +.mb-size-lg { | ||
51 | + margin-bottom: 20px; | ||
52 | +} | ||
53 | + | ||
54 | +.margin-size-default{ | ||
55 | + margin: 8px; | ||
56 | +} | ||
57 | +.margin-size-sm { | ||
58 | + margin: 4px; | ||
59 | +} | ||
60 | +.margin-size-md { | ||
61 | + margin: 12px; | ||
62 | +} | ||
63 | +.margin-size-lg { | ||
64 | + margin: 20px; | ||
65 | +} | ||
66 | + | ||
67 | + | ||
68 | +.pt-size-default{ | ||
69 | + padding-top: 8px; | ||
70 | +} | ||
71 | +.pt-size-sm { | ||
72 | + padding-top: 4px; | ||
73 | +} | ||
74 | +.pt-size-md { | ||
75 | + padding-top: 12px; | ||
76 | +} | ||
77 | +.pt-size-lg { | ||
78 | + padding-top: 20px; | ||
79 | +} | ||
80 | + | ||
81 | +.pl-size-default{ | ||
82 | + padding-left: 8px; | ||
83 | +} | ||
84 | +.pl-size-sm { | ||
85 | + padding-left: 4px; | ||
86 | +} | ||
87 | +.pl-size-md { | ||
88 | + padding-left: 12px; | ||
89 | +} | ||
90 | +.pl-size-lg { | ||
91 | + padding-left: 20px; | ||
92 | +} | ||
93 | + | ||
94 | +.pr-size-default{ | ||
95 | + padding-right: 8px; | ||
96 | +} | ||
97 | +.pr-size-sm { | ||
98 | + padding-right: 4px; | ||
99 | +} | ||
100 | +.pr-size-md { | ||
101 | + padding-right: 12px; | ||
102 | +} | ||
103 | +.pr-size-lg { | ||
104 | + padding-right: 20px; | ||
105 | +} | ||
106 | + | ||
107 | +.pb-size-default{ | ||
108 | + padding-bottom: 8px; | ||
109 | +} | ||
110 | +.pb-size-sm { | ||
111 | + padding-bottom: 4px; | ||
112 | +} | ||
113 | +.pb-size-md { | ||
114 | + padding-bottom: 12px; | ||
115 | +} | ||
116 | +.pb-size-lg { | ||
117 | + padding-bottom: 20px; | ||
118 | +} | ||
119 | + | ||
120 | +.padding-size-default{ | ||
121 | + padding: 8px; | ||
122 | +} | ||
123 | +.padding-size-sm { | ||
124 | + padding: 4px; | ||
125 | +} | ||
126 | +.padding-size-md { | ||
127 | + padding: 12px; | ||
128 | +} | ||
129 | +.padding-size-lg { | ||
130 | + padding: 20px; | ||
131 | +} | ||
132 | +.r-flex{ | ||
133 | + display: flex; | ||
134 | + &.center{ | ||
135 | + justify-content: center; | ||
136 | + align-items: center; | ||
137 | + } | ||
138 | + &.space-between{ | ||
139 | + justify-content: space-between; | ||
140 | + align-items: center; | ||
141 | + } | ||
142 | + &.space-around{ | ||
143 | + justify-content: space-around; | ||
144 | + align-items: center; | ||
145 | + } | ||
146 | + &.space-evenly{ | ||
147 | + justify-content: space-evenly; | ||
148 | + align-items: center; | ||
149 | + } | ||
150 | + &.start{ | ||
151 | + justify-content: start; | ||
152 | + align-items: center; | ||
153 | + } | ||
154 | + &.end{ | ||
155 | + justify-content: flex-end; | ||
156 | + align-items: center; | ||
157 | + } | ||
158 | + &.gap-1 { | ||
159 | + gap: 4px | ||
160 | + } | ||
161 | + &.gap-2 { | ||
162 | + gap: 8px | ||
163 | + } | ||
164 | + &.gap-3 { | ||
165 | + gap: 12px | ||
166 | + } | ||
167 | + &.gap-4 { | ||
168 | + gap: 16px | ||
169 | + } | ||
170 | + &.gap-5 { | ||
171 | + gap: 20px | ||
172 | + } | ||
173 | + &.gap-6 { | ||
174 | + gap: 24px | ||
175 | + } | ||
176 | + &.gap-default{ | ||
177 | + gap: 8px | ||
178 | + } | ||
179 | + &.gap-sm { | ||
180 | + gap: 6px | ||
181 | + } | ||
182 | + &.gap-md { | ||
183 | + gap: 12px | ||
184 | + } | ||
185 | + &.gap-lg { | ||
186 | + gap: 20px | ||
187 | + } | ||
188 | +} |
1 | +@import '@/assets/styles/mixins.scss'; | ||
2 | + | ||
3 | +.page-header { | ||
4 | + background-color: white; | ||
5 | + border: $gray-3 1px solid; | ||
6 | + border-radius: 6px; | ||
7 | +} | ||
8 | + | ||
9 | +.search-input { | ||
10 | + width: 50%; | ||
11 | + min-width: 200px; | ||
12 | +} | ||
13 | + | ||
14 | +.page-main { | ||
15 | + margin-top: 20px; | ||
16 | + padding: 20px 20px 20px 20px; | ||
17 | + background-color: white; | ||
18 | + border-radius: 6px; | ||
19 | + border: $gray-2 1px solid; | ||
20 | +} | ||
21 | + | ||
22 | +.page-main-without-header { | ||
23 | + padding: 20px 20px 20px 20px; | ||
24 | + background-color: white; | ||
25 | + border-radius: 6px; | ||
26 | + border: $gray-2 1px solid; | ||
27 | +} |
1 | +.ant-table-pagination.ant-pagination { | ||
2 | + float: none; | ||
3 | + text-align: center; | ||
4 | +} | ||
5 | + | ||
6 | +.ant-pagination-prev .ant-pagination-item-link, | ||
7 | +.ant-pagination-next .ant-pagination-item-link, | ||
8 | +.ant-pagination-item { | ||
9 | + border: none; | ||
10 | + outline: none; | ||
11 | +} | ||
12 | + | ||
13 | +.ant-pagination-item { | ||
14 | + font-size: 1rem; | ||
15 | +} | ||
16 | + | ||
17 | +.ant-pagination-item-active a { | ||
18 | + font-weight: bolder; | ||
19 | +} |
1 | +@import '@/assets/styles/mixins.scss'; |
1 | +.default-layout-enter-active, | ||
2 | +.default-layout-leave-active { | ||
3 | + transition: opacity 0.5s; | ||
4 | +} | ||
5 | +.default-layout-enter, | ||
6 | +.default-layout-leave-active { | ||
7 | + opacity: 0; | ||
8 | +} | ||
9 | +// | ||
10 | +.default-page-enter-active, | ||
11 | +.default-page-leave-active { | ||
12 | + transition: opacity 0.3s; | ||
13 | +} | ||
14 | +.default-page-enter, | ||
15 | +.default-page-leave-active { | ||
16 | + opacity: 0; | ||
17 | +} |
1 | +<template> | ||
2 | + <pie-chart :chart-data="chartData" | ||
3 | + :chart-settings="chartSettings" | ||
4 | + /> | ||
5 | +</template> | ||
6 | + | ||
7 | +<script> | ||
8 | +import PieChart from '@/components/_Common/Chart/pieChart'; | ||
9 | + | ||
10 | +export default { | ||
11 | + name: 'droneCategoryPieChart', | ||
12 | + components: { | ||
13 | + PieChart, | ||
14 | + }, | ||
15 | + props: { | ||
16 | + chartData: { | ||
17 | + type: Array, | ||
18 | + default: null, | ||
19 | + }, | ||
20 | + }, | ||
21 | + data() { | ||
22 | + return { | ||
23 | + chartSettings: { | ||
24 | + chart: { | ||
25 | + height: 400, | ||
26 | + }, | ||
27 | + title: { | ||
28 | + text: '카테고리별 드론 기체 수', | ||
29 | + style: { | ||
30 | + fontSize: '20px', | ||
31 | + }, | ||
32 | + }, | ||
33 | + width: '50%', | ||
34 | + plotOptions: { | ||
35 | + pie: { | ||
36 | + shadow: true, | ||
37 | + allowPointSelect: true, | ||
38 | + cursor: 'pointer', | ||
39 | + dataLabels: { | ||
40 | + useHTML: true, | ||
41 | + distance: -50, | ||
42 | + formatter() { | ||
43 | + if (this.percentage.toFixed(0) < 6) return ''; | ||
44 | + return `<div style="padding: 6px 4px 4px 6px; | ||
45 | + background-color: rgba(0, 0, 0, 0.5); | ||
46 | + border: 2px solid #f2f4f8; | ||
47 | + border-radius: 6px; | ||
48 | + ">${this.percentage.toFixed(1)}%</div>`; | ||
49 | + }, | ||
50 | + style: { | ||
51 | + fontSize: '16px', | ||
52 | + color: 'white', | ||
53 | + }, | ||
54 | + }, | ||
55 | + showInLegend: true, | ||
56 | + }, | ||
57 | + }, | ||
58 | + tooltip: { | ||
59 | + formatter() { | ||
60 | + return ` | ||
61 | + <span style="font-size:16px; color:${this.color}">${this.key}</span> | ||
62 | + <table> | ||
63 | + <tr> | ||
64 | + <td style="padding:0">${this.series.name}</td> | ||
65 | + <td style="padding:0">: <b>${this.y}</b></td> | ||
66 | + </tr> | ||
67 | + <tr> | ||
68 | + <td style="padding:0">점유율</td> | ||
69 | + <td style="color:{series.color};padding:0">: <b>${this.percentage.toFixed(1)}%</b></td> | ||
70 | + </tr> | ||
71 | + </table> | ||
72 | + `; | ||
73 | + }, | ||
74 | + }, | ||
75 | + }, | ||
76 | + }; | ||
77 | + }, | ||
78 | +}; | ||
79 | +</script> | ||
80 | + | ||
81 | +<style scoped> | ||
82 | + | ||
83 | +</style> |
1 | +<template> | ||
2 | + <live-line-chart :chart-data="chartData" | ||
3 | + :chart-settings="chartSettings" | ||
4 | + /> | ||
5 | +</template> | ||
6 | + | ||
7 | +<script> | ||
8 | +import LiveLineChart from '@/components/_Common/Chart/liveLineChart'; | ||
9 | +import dayjs from 'dayjs'; | ||
10 | + | ||
11 | +export default { | ||
12 | + components: { | ||
13 | + LiveLineChart, | ||
14 | + }, | ||
15 | + props: { | ||
16 | + chartData: { | ||
17 | + type: Array, | ||
18 | + default: null, | ||
19 | + }, | ||
20 | + }, | ||
21 | + data() { | ||
22 | + return { | ||
23 | + chartSettings: { | ||
24 | + chart: { | ||
25 | + height: 400, | ||
26 | + }, | ||
27 | + title: { | ||
28 | + text: '시간별 드론 기체 수', | ||
29 | + style: { | ||
30 | + fontSize: '20px', | ||
31 | + }, | ||
32 | + }, | ||
33 | + width: '100%', | ||
34 | + xAxis: { | ||
35 | + type: 'datetime', | ||
36 | + }, | ||
37 | + yAxis: { | ||
38 | + title: { | ||
39 | + text: null, | ||
40 | + }, | ||
41 | + min: 0, | ||
42 | + }, | ||
43 | + tooltip: { | ||
44 | + formatter() { | ||
45 | + const trList = this.points.map((elem) => ` | ||
46 | + <tr> | ||
47 | + <td style="font-size: 16px; padding:0; color:${elem.color}">${elem.series.name}</td> | ||
48 | + <td style="font-size: 16px; padding:0; color:${elem.color}">: <b>${elem.y}</b></td> | ||
49 | + </tr> | ||
50 | + `); | ||
51 | + return ` | ||
52 | + <span style="font-size:12px; color:${this.color}">${dayjs(this.x).format('YYYY-MM-DD HH:mm:ss')}</span> | ||
53 | + <table> | ||
54 | + ${trList} | ||
55 | + </table> | ||
56 | + `; | ||
57 | + }, | ||
58 | + }, | ||
59 | + data: { | ||
60 | + enablePolling: true, | ||
61 | + dataRefreshRate: 2, | ||
62 | + }, | ||
63 | + }, | ||
64 | + }; | ||
65 | + }, | ||
66 | +}; | ||
67 | +</script> | ||
68 | + | ||
69 | +<style scoped lang="scss"> | ||
70 | + | ||
71 | +</style> |
1 | +<template> | ||
2 | + <pie-chart :chart-data="chartData" | ||
3 | + :chart-settings="chartSettings" | ||
4 | + /> | ||
5 | +</template> | ||
6 | + | ||
7 | +<script> | ||
8 | +import PieChart from '@/components/_Common/Chart/pieChart'; | ||
9 | + | ||
10 | +export default { | ||
11 | + name: 'makerPieChart', | ||
12 | + components: { | ||
13 | + PieChart, | ||
14 | + }, | ||
15 | + props: { | ||
16 | + chartData: { | ||
17 | + type: Array, | ||
18 | + default: null, | ||
19 | + }, | ||
20 | + }, | ||
21 | + data() { | ||
22 | + return { | ||
23 | + chartSettings: { | ||
24 | + chart: { | ||
25 | + height: 400, | ||
26 | + }, | ||
27 | + title: { | ||
28 | + text: '제조사별 드론 기체 수', | ||
29 | + style: { | ||
30 | + fontSize: '20px', | ||
31 | + }, | ||
32 | + }, | ||
33 | + width: '50%', | ||
34 | + plotOptions: { | ||
35 | + pie: { | ||
36 | + shadow: true, | ||
37 | + allowPointSelect: true, | ||
38 | + cursor: 'pointer', | ||
39 | + dataLabels: { | ||
40 | + useHTML: true, | ||
41 | + distance: -50, | ||
42 | + formatter() { | ||
43 | + if (this.percentage.toFixed(0) < 6) return ''; | ||
44 | + return `<div style="padding: 6px 4px 4px 6px; | ||
45 | + background-color: rgba(0, 0, 0, 0.5); | ||
46 | + border: 2px solid #f2f4f8; | ||
47 | + border-radius: 6px; | ||
48 | + ">${this.percentage.toFixed(1)}%</div>`; | ||
49 | + }, | ||
50 | + style: { | ||
51 | + fontSize: '16px', | ||
52 | + color: 'white', | ||
53 | + }, | ||
54 | + }, | ||
55 | + showInLegend: true, | ||
56 | + }, | ||
57 | + }, | ||
58 | + tooltip: { | ||
59 | + formatter() { | ||
60 | + return ` | ||
61 | + <span style="font-size:16px; color:${this.color}">${this.key}</span> | ||
62 | + <table> | ||
63 | + <tr> | ||
64 | + <td style="padding:0">${this.series.name}</td> | ||
65 | + <td style="padding:0">: <b>${this.y}</b></td> | ||
66 | + </tr> | ||
67 | + <tr> | ||
68 | + <td style="padding:0">점유율</td> | ||
69 | + <td style="color:{series.color};padding:0">: <b>${this.percentage.toFixed(1)}%</b></td> | ||
70 | + </tr> | ||
71 | + </table> | ||
72 | + `; | ||
73 | + }, | ||
74 | + }, | ||
75 | + }, | ||
76 | + }; | ||
77 | + }, | ||
78 | +}; | ||
79 | +</script> | ||
80 | + | ||
81 | +<style scoped> | ||
82 | + | ||
83 | +</style> |
1 | +<template> | ||
2 | + <column-chart :chart-data="chartData" | ||
3 | + :chart-settings="chartSettings"/> | ||
4 | +</template> | ||
5 | + | ||
6 | +<script> | ||
7 | +import ColumnChart from '@/components/_Common/Chart/columnChart'; | ||
8 | + | ||
9 | +export default { | ||
10 | + name: 'timeCategoryColumnChart', | ||
11 | + components: { | ||
12 | + ColumnChart, | ||
13 | + | ||
14 | + }, | ||
15 | + props: { | ||
16 | + chartData: { | ||
17 | + type: Array, | ||
18 | + default: () => [], | ||
19 | + }, | ||
20 | + }, | ||
21 | + data() { | ||
22 | + return { | ||
23 | + chartSettings: { | ||
24 | + chart: { | ||
25 | + height: 400, | ||
26 | + }, | ||
27 | + title: { | ||
28 | + text: '시간-드론 타입별 드론 기체 수', | ||
29 | + style: { | ||
30 | + fontSize: '20px', | ||
31 | + }, | ||
32 | + }, | ||
33 | + width: '100%', | ||
34 | + xAxis: { | ||
35 | + categories: [ | ||
36 | + '00:00 ~ 04:00', | ||
37 | + '04:00 ~ 08:00', | ||
38 | + '08:00 ~ 12:00', | ||
39 | + '12:00 ~ 16:00', | ||
40 | + '16:00 ~ 20:00', | ||
41 | + '20:00 ~ 24:00', | ||
42 | + ], | ||
43 | + }, | ||
44 | + yAxis: { | ||
45 | + title: { | ||
46 | + text: null, | ||
47 | + }, | ||
48 | + min: 0, | ||
49 | + }, | ||
50 | + }, | ||
51 | + }; | ||
52 | + }, | ||
53 | +}; | ||
54 | +</script> | ||
55 | + | ||
56 | +<style scoped lang="scss"> | ||
57 | + | ||
58 | +</style> |
Frontend/src/components/Analytics/header.vue
0 → 100644
1 | +<template> | ||
2 | + <a-page-header | ||
3 | + class="page-header" | ||
4 | + title="Analytics" | ||
5 | + sub-title="Chart for Realtime Drone Data" | ||
6 | + > | ||
7 | + <template slot="extra"> | ||
8 | + <a-button key="1" type="primary"> | ||
9 | + action | ||
10 | + </a-button> | ||
11 | + </template> | ||
12 | + </a-page-header> | ||
13 | +</template> | ||
14 | + | ||
15 | +<script> | ||
16 | + | ||
17 | +export default { | ||
18 | + name: 'AnalyticsHeader', | ||
19 | + components: {}, | ||
20 | + data() { | ||
21 | + return { | ||
22 | + }; | ||
23 | + }, | ||
24 | +}; | ||
25 | +</script> | ||
26 | + | ||
27 | +<style scoped lang="scss"> | ||
28 | + | ||
29 | +</style> |
1 | +<template> | ||
2 | + <a-page-header | ||
3 | + class="page-header" | ||
4 | + title="드론 정보" | ||
5 | + sub-title="Drone-Schedule" | ||
6 | + @back="$router.go(-1)" | ||
7 | + > | ||
8 | + <div :style="{display: 'flex'}"> | ||
9 | + <div :style="{width: '30%'}"> | ||
10 | + <img :src="droneInfo.picture" :style="{width: '90%'}" /> | ||
11 | + </div> | ||
12 | + | ||
13 | + <div :style="{width: '70%', marginLeft: '20px'}"> | ||
14 | + <div class="label-modelName">{{ droneInfo.modelName }}</div> | ||
15 | + <div :style="{display: 'flex', marginBottom: '15px'}"> | ||
16 | + <div class="label-info">info</div> | ||
17 | + | ||
18 | + <a-descriptions | ||
19 | + :column="{xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1}" | ||
20 | + class="description-box" | ||
21 | + > | ||
22 | + <a-descriptions-item label="모델명"> | ||
23 | + {{ droneInfo.modelName }} | ||
24 | + </a-descriptions-item> | ||
25 | + <a-descriptions-item label="제조사"> | ||
26 | + {{ droneInfo.maker == null ? 'None' : droneInfo.maker }} | ||
27 | + </a-descriptions-item> | ||
28 | + <a-descriptions-item label="종류"> | ||
29 | + {{ droneInfo.usage == null ? 'None' : droneInfo.usage }} | ||
30 | + </a-descriptions-item> | ||
31 | + <a-descriptions-item label="제원"> | ||
32 | + {{ | ||
33 | + droneInfo.specification == null | ||
34 | + ? 'None' | ||
35 | + : droneInfo.specification | ||
36 | + }} | ||
37 | + </a-descriptions-item> | ||
38 | + <a-descriptions-item label="무게"> | ||
39 | + {{ droneInfo.weight == null ? '?' : droneInfo.weight }} kg | ||
40 | + </a-descriptions-item> | ||
41 | + <a-descriptions-item label="No"> | ||
42 | + {{ droneInfo.id }} | ||
43 | + </a-descriptions-item> | ||
44 | + </a-descriptions> | ||
45 | + </div> | ||
46 | + </div> | ||
47 | + </div> | ||
48 | + <a-divider /> | ||
49 | + </a-page-header> | ||
50 | +</template> | ||
51 | + | ||
52 | +<script> | ||
53 | +import {mapGetters} from 'vuex'; | ||
54 | + | ||
55 | +export default { | ||
56 | + name: 'DatabaseDetailHeader', | ||
57 | + components: {}, | ||
58 | + data() { | ||
59 | + return { | ||
60 | + droneInfo: {}, | ||
61 | + }; | ||
62 | + }, | ||
63 | + computed: { | ||
64 | + ...mapGetters('Drone/detail', { | ||
65 | + getDetailData: 'getDetailData', | ||
66 | + }), | ||
67 | + ...mapGetters('Code', { | ||
68 | + getCodes: 'getCodes', | ||
69 | + }), | ||
70 | + droneCategory() { | ||
71 | + return (data) => { | ||
72 | + switch (parseInt(data, 10)) { | ||
73 | + case 1: | ||
74 | + return '촬영용'; | ||
75 | + case 2: | ||
76 | + return '레이싱용'; | ||
77 | + case 3: | ||
78 | + return '완구용'; | ||
79 | + default: | ||
80 | + return null; | ||
81 | + } | ||
82 | + }; | ||
83 | + }, | ||
84 | + }, | ||
85 | + created() { | ||
86 | + console.log(this.getDetailData); | ||
87 | + this.droneInfo = this.getDetailData; | ||
88 | + }, | ||
89 | +}; | ||
90 | +</script> | ||
91 | + | ||
92 | +<style scoped lang="scss"> | ||
93 | +@import '@/assets/styles/mixins.scss'; | ||
94 | + | ||
95 | +.label-modelName { | ||
96 | + font-size: 30px; | ||
97 | + color: $antblue; | ||
98 | +} | ||
99 | +.label-info { | ||
100 | + padding: 0 10px; | ||
101 | + border-right: 1px solid #777777; | ||
102 | + min-width: 20%; | ||
103 | +} | ||
104 | +.description-box { | ||
105 | + padding-left: 10px; | ||
106 | +} | ||
107 | +</style> |
1 | +<template> | ||
2 | + <a-page-header | ||
3 | + class="page-header" | ||
4 | + title="Database" | ||
5 | + sub-title="Table - Drone list" | ||
6 | + > | ||
7 | + <template slot="extra"> | ||
8 | + <a-button key="1" type="primary" form="form" html-type="submit"> | ||
9 | + 검색 | ||
10 | + </a-button> | ||
11 | + </template> | ||
12 | + | ||
13 | + <a-form id="form" @submit.prevent="searchData" class="form-wrapper"> | ||
14 | + <a-form-item label="모델명" class="form-item"> | ||
15 | + <a-input v-model="searchParams.modelName" class="search-input" /> | ||
16 | + </a-form-item> | ||
17 | + <a-form-item label="제조사" class="form-item"> | ||
18 | + <a-input v-model="searchParams.maker" class="search-input" /> | ||
19 | + </a-form-item> | ||
20 | + <a-form-item label="무게" class="form-item"> | ||
21 | + <a-button | ||
22 | + v-if="searchOpenFlag" | ||
23 | + class="search-input" | ||
24 | + @click="searchOpenFlag = !searchOpenFlag" | ||
25 | + >{{ sliderBegin }} - {{ sliderEnd == 50 ? '50+' : sliderEnd }} kg | ||
26 | + <a-icon type="up" /> | ||
27 | + </a-button> | ||
28 | + <a-button | ||
29 | + v-else | ||
30 | + class="search-input" | ||
31 | + @click="searchOpenFlag = !searchOpenFlag" | ||
32 | + >{{ sliderBegin }} - {{ sliderEnd == 50 ? '50+' : sliderEnd }} kg | ||
33 | + <a-icon type="down" /> | ||
34 | + </a-button> | ||
35 | + <div v-if="searchOpenFlag" class="slider-box"> | ||
36 | + <a-slider | ||
37 | + range | ||
38 | + :marks="marks" | ||
39 | + :max="50" | ||
40 | + :step="1" | ||
41 | + :default-value="[sliderBegin, sliderEnd]" | ||
42 | + class="search-input" | ||
43 | + @change="onSliderChange" | ||
44 | + @afterChange="onSliderAfterChange" | ||
45 | + /> | ||
46 | + </div> | ||
47 | + </a-form-item> | ||
48 | + <a-form-item label="종류" has-feedback class="form-item"> | ||
49 | + <a-select | ||
50 | + class="search-input" | ||
51 | + default-value="" | ||
52 | + @change="handleSelectChange" | ||
53 | + > | ||
54 | + <a-select-option value=""> | ||
55 | + 선택 안 함 | ||
56 | + </a-select-option> | ||
57 | + <a-select-option value="촬영용"> | ||
58 | + 촬영용 | ||
59 | + </a-select-option> | ||
60 | + <a-select-option value="산업용"> | ||
61 | + 산업용 | ||
62 | + </a-select-option> | ||
63 | + <a-select-option value="군사용"> | ||
64 | + 군사용 | ||
65 | + </a-select-option> | ||
66 | + <a-select-option value="레이싱용"> | ||
67 | + 레이싱용 | ||
68 | + </a-select-option> | ||
69 | + </a-select> | ||
70 | + </a-form-item> | ||
71 | + </a-form> | ||
72 | + </a-page-header> | ||
73 | +</template> | ||
74 | + | ||
75 | +<script> | ||
76 | +import {droneCategory} from '@/utils/CommonData/selectOptions'; | ||
77 | +import {mapActions, mapGetters} from 'vuex'; | ||
78 | + | ||
79 | +export default { | ||
80 | + name: 'DatabaseSearchFilter', | ||
81 | + components: {}, | ||
82 | + data() { | ||
83 | + return { | ||
84 | + searchOpenFlag: false, | ||
85 | + categoryOptions: droneCategory, | ||
86 | + searchParams: {}, | ||
87 | + marks: { | ||
88 | + 0: '0kg', | ||
89 | + 50: '50+kg', | ||
90 | + }, | ||
91 | + sliderBegin: 0, | ||
92 | + sliderEnd: 50, | ||
93 | + }; | ||
94 | + }, | ||
95 | + computed: { | ||
96 | + ...mapGetters('Drone/page', { | ||
97 | + getPageParams: 'getPageParams', | ||
98 | + }), | ||
99 | + }, | ||
100 | + created() { | ||
101 | + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams)); | ||
102 | + }, | ||
103 | + methods: { | ||
104 | + ...mapActions('Drone/page', { | ||
105 | + setPageParams: 'setPageParams', | ||
106 | + }), | ||
107 | + searchData() { | ||
108 | + console.log(this.searchParams); | ||
109 | + this.setPageParams(this.searchParams); | ||
110 | + this.$emit('loadData'); | ||
111 | + }, | ||
112 | + handleSelectChange(value) { | ||
113 | + this.searchParams.usageName = value; | ||
114 | + this.searchData(); | ||
115 | + }, | ||
116 | + onSliderChange(value) { | ||
117 | + this.sliderBegin = value[0]; | ||
118 | + this.sliderEnd = value[1]; | ||
119 | + }, | ||
120 | + onSliderAfterChange(value) { | ||
121 | + this.searchParams.minWeight = value[0]; | ||
122 | + this.searchParams.maxWeight = value[1]; | ||
123 | + if (value[1] == 50) { | ||
124 | + this.searchParams.maxWeight = null; | ||
125 | + } | ||
126 | + this.searchOpenFlag = !this.searchOpenFlag; | ||
127 | + }, | ||
128 | + }, | ||
129 | +}; | ||
130 | +</script> | ||
131 | + | ||
132 | +<style scoped lang="scss"> | ||
133 | +@import '@/assets/styles/mixins.scss'; | ||
134 | + | ||
135 | +.form-wrapper { | ||
136 | + height: 40px; | ||
137 | +} | ||
138 | +.form-wrapper, | ||
139 | +.form-item { | ||
140 | + display: flex; | ||
141 | + margin-left: 10px; | ||
142 | + margin-bottom: 0; | ||
143 | +} | ||
144 | + | ||
145 | +.slider-box { | ||
146 | + background: white; | ||
147 | + padding: 10px 20px; | ||
148 | + border-radius: 20px; | ||
149 | + border: solid 1px $antblue; | ||
150 | + position: fixed; | ||
151 | + z-index: 10; | ||
152 | +} | ||
153 | +</style> |
1 | +<template> | ||
2 | + <a-table | ||
3 | + rowKey="id" | ||
4 | + bordered | ||
5 | + :loading="childLoading" | ||
6 | + :columns="columns" | ||
7 | + :data-source="getPageData" | ||
8 | + :scroll="{x: 1000}" | ||
9 | + :pagination="pagination" | ||
10 | + @change="changePage" | ||
11 | + > | ||
12 | + <a slot="modelName" slot-scope="data, row" @click="goDetail(row)"> | ||
13 | + {{ data }} | ||
14 | + </a> | ||
15 | + <div slot="usageName" slot-scope="data"> | ||
16 | + {{ data == null ? '?' : data }} | ||
17 | + </div> | ||
18 | + <div slot="weight" slot-scope="data"> | ||
19 | + {{ data == null ? '?' : data + 'kg' }} | ||
20 | + </div> | ||
21 | + <div slot="specification" slot-scope="data"> | ||
22 | + {{ data == null ? '?' : data }} | ||
23 | + </div> | ||
24 | + <div slot="droneCategory" slot-scope="data"> | ||
25 | + {{ data }} | ||
26 | + </div> | ||
27 | + </a-table> | ||
28 | +</template> | ||
29 | + | ||
30 | +<script> | ||
31 | +import databaseColumn from '@/utils/ColumnData/databaseColumn'; | ||
32 | +import { mapActions, mapGetters } from 'vuex'; | ||
33 | + | ||
34 | +export default { | ||
35 | + name: 'DatabaseTable', | ||
36 | + props: { | ||
37 | + childLoading: { | ||
38 | + type: Boolean, | ||
39 | + default: false, | ||
40 | + }, | ||
41 | + }, | ||
42 | + data() { | ||
43 | + return { | ||
44 | + columns: databaseColumn, | ||
45 | + }; | ||
46 | + }, | ||
47 | + computed: { | ||
48 | + ...mapGetters('Drone/page', { | ||
49 | + getPageData: 'getPageData', | ||
50 | + getPagination: 'getPagination', | ||
51 | + getPageParams: 'getPageParams', | ||
52 | + }), | ||
53 | + pagination: { | ||
54 | + get() { | ||
55 | + return this.getPagination; | ||
56 | + }, | ||
57 | + set(e) { | ||
58 | + this.setPagination({ | ||
59 | + size: e.size, | ||
60 | + page: e.page, | ||
61 | + }); | ||
62 | + }, | ||
63 | + }, | ||
64 | + }, | ||
65 | + methods: { | ||
66 | + ...mapActions('Drone/page', { | ||
67 | + setPagination: 'setPagination', | ||
68 | + fetchPageData: 'fetchPageData', | ||
69 | + }), | ||
70 | + changePage(e) { | ||
71 | + this.pagination = { | ||
72 | + size: e.pageSize, | ||
73 | + page: e.current, | ||
74 | + }; | ||
75 | + this.fetchPageData(this.getPageParams); | ||
76 | + }, | ||
77 | + goDetail(row) { | ||
78 | + this.$router.push({ | ||
79 | + path: `/database/drone/${row.id}`, | ||
80 | + }); | ||
81 | + }, | ||
82 | + }, | ||
83 | +}; | ||
84 | +</script> | ||
85 | + | ||
86 | +<style scoped lang="scss"></style> |
1 | +<template> | ||
2 | + <a-page-header | ||
3 | + class="page-header" | ||
4 | + title="Database" | ||
5 | + sub-title="Table - Drone Log" | ||
6 | + > | ||
7 | + <template slot="extra"> | ||
8 | + <a-button | ||
9 | + key="1" | ||
10 | + type="primary" | ||
11 | + form="form" | ||
12 | + html-type="submit" | ||
13 | + :style="{padding: '0 15px'}" | ||
14 | + > | ||
15 | + 검색 | ||
16 | + </a-button> | ||
17 | + </template> | ||
18 | + | ||
19 | + <a-form id="form" @submit.prevent="searchData" class="form-wrapper"> | ||
20 | + <a-descriptions | ||
21 | + :column="{xs: 1, sm: 3, md: 3, lg: 6, xl: 7, xxl: 7}" | ||
22 | + class="search-box" | ||
23 | + > | ||
24 | + <a-descriptions-item label="Schedule ID" class="form-item"> | ||
25 | + <a-input v-model="searchParams.scheduleId" class="search-input-log" /> | ||
26 | + </a-descriptions-item> | ||
27 | + <a-descriptions-item label="수평 속도" class="form-item"> | ||
28 | + <a-button v-if="hsOpenFlag" @click="hsOpenFlag = !hsOpenFlag" | ||
29 | + >{{ minHorizontalSpeed }} - | ||
30 | + {{ maxHorizontalSpeed == 100 ? '100+' : maxHorizontalSpeed }} km/s | ||
31 | + <a-icon type="up" /> | ||
32 | + </a-button> | ||
33 | + <a-button v-else @click="hsOpenFlag = !hsOpenFlag" | ||
34 | + >{{ minHorizontalSpeed }} - | ||
35 | + {{ maxHorizontalSpeed == 100 ? '100+' : maxHorizontalSpeed }} km/s | ||
36 | + <a-icon type="down" /> | ||
37 | + </a-button> | ||
38 | + <div v-if="hsOpenFlag" class="slider-box"> | ||
39 | + <a-slider | ||
40 | + range | ||
41 | + :marks="speedMarks" | ||
42 | + :max="100" | ||
43 | + :step="1" | ||
44 | + :default-value="[minHorizontalSpeed, maxHorizontalSpeed]" | ||
45 | + class="search-input" | ||
46 | + @change="onHorizontalSpeedChange" | ||
47 | + @afterChange="onHorizontalSpeedAfterChange" | ||
48 | + /> | ||
49 | + </div> | ||
50 | + </a-descriptions-item> | ||
51 | + <a-descriptions-item label="수직 속도" class="form-item"> | ||
52 | + <a-button v-if="vsOpenFlag" @click="vsOpenFlag = !vsOpenFlag" | ||
53 | + >{{ minVerticalSpeed }} - | ||
54 | + {{ maxVerticalSpeed == 100 ? '100+' : maxVerticalSpeed }} km/s | ||
55 | + <a-icon type="up" /> | ||
56 | + </a-button> | ||
57 | + <a-button v-else @click="vsOpenFlag = !vsOpenFlag" | ||
58 | + >{{ minVerticalSpeed }} - | ||
59 | + {{ maxVerticalSpeed == 100 ? '100+' : maxVerticalSpeed }} km/s | ||
60 | + <a-icon type="down" /> | ||
61 | + </a-button> | ||
62 | + <div v-if="vsOpenFlag" class="slider-box"> | ||
63 | + <a-slider | ||
64 | + range | ||
65 | + :marks="speedMarks" | ||
66 | + :max="100" | ||
67 | + :step="1" | ||
68 | + :default-value="[minVerticalSpeed, maxVerticalSpeed]" | ||
69 | + class="search-input" | ||
70 | + @change="onVerticalSpeedChange" | ||
71 | + @afterChange="onVerticalSpeedAfterChange" | ||
72 | + /> | ||
73 | + </div> | ||
74 | + </a-descriptions-item> | ||
75 | + <a-descriptions-item label="지면 고도" class="form-item"> | ||
76 | + <a-button v-if="aglOpenFlag" @click="aglOpenFlag = !aglOpenFlag" | ||
77 | + >{{ minAboveGroundLevel }} - | ||
78 | + {{ maxAboveGroundLevel == 200 ? '200+' : maxAboveGroundLevel }} m | ||
79 | + <a-icon type="up" /> | ||
80 | + </a-button> | ||
81 | + <a-button v-else @click="aglOpenFlag = !aglOpenFlag" | ||
82 | + >{{ minAboveGroundLevel }} - | ||
83 | + {{ maxAboveGroundLevel == 200 ? '200+' : maxAboveGroundLevel }} m | ||
84 | + <a-icon type="down" /> | ||
85 | + </a-button> | ||
86 | + <div v-if="aglOpenFlag" class="slider-box"> | ||
87 | + <a-slider | ||
88 | + range | ||
89 | + :marks="levelMarks" | ||
90 | + :max="200" | ||
91 | + :step="1" | ||
92 | + :default-value="[minAboveGroundLevel, maxAboveGroundLevel]" | ||
93 | + class="search-input" | ||
94 | + @change="onAboveGroundLevelChange" | ||
95 | + @afterChange="onAboveGroundLevelAfterChange" | ||
96 | + /> | ||
97 | + </div> | ||
98 | + </a-descriptions-item> | ||
99 | + <a-descriptions-item label="해발 고도" class="form-item"> | ||
100 | + <a-button v-if="aslOpenFlag" @click="aslOpenFlag = !aslOpenFlag" | ||
101 | + >{{ minAboveSeaLevel }} - | ||
102 | + {{ maxAboveSeaLevel == 200 ? '200+' : maxAboveSeaLevel }} m | ||
103 | + <a-icon type="up" /> | ||
104 | + </a-button> | ||
105 | + <a-button v-else @click="aslOpenFlag = !aslOpenFlag" | ||
106 | + >{{ minAboveSeaLevel }} - | ||
107 | + {{ maxAboveSeaLevel == 200 ? '200+' : maxAboveSeaLevel }} m | ||
108 | + <a-icon type="down" /> | ||
109 | + </a-button> | ||
110 | + <div v-if="aslOpenFlag" class="slider-box"> | ||
111 | + <a-slider | ||
112 | + range | ||
113 | + :marks="levelMarks" | ||
114 | + :max="200" | ||
115 | + :step="1" | ||
116 | + :default-value="[minAboveSeaLevel, maxAboveSeaLevel]" | ||
117 | + class="search-input" | ||
118 | + @change="onAboveSeaLevelChange" | ||
119 | + @afterChange="onAboveSeaLevelAfterChange" | ||
120 | + /> | ||
121 | + </div> | ||
122 | + </a-descriptions-item> | ||
123 | + <a-descriptions-item label="위도" class="form-item"> | ||
124 | + <a-input v-model="searchParams.latitude" class="search-input-log" /> | ||
125 | + </a-descriptions-item> | ||
126 | + <a-descriptions-item label="경도" class="form-item"> | ||
127 | + <a-input v-model="searchParams.longitude" class="search-input-log" /> | ||
128 | + </a-descriptions-item> | ||
129 | + </a-descriptions> | ||
130 | + </a-form> | ||
131 | + </a-page-header> | ||
132 | +</template> | ||
133 | + | ||
134 | +<script> | ||
135 | +/* eslint-disable prefer-destructuring */ | ||
136 | +import {mapActions, mapGetters} from 'vuex'; | ||
137 | + | ||
138 | +export default { | ||
139 | + name: 'LogSearchFilter', | ||
140 | + components: {}, | ||
141 | + data() { | ||
142 | + return { | ||
143 | + hsOpenFlag: false, | ||
144 | + vsOpenFlag: false, | ||
145 | + aslOpenFlag: false, | ||
146 | + aglOpenFlag: false, | ||
147 | + searchParams: {}, | ||
148 | + speedMarks: { | ||
149 | + 0: '0km/h', | ||
150 | + 100: '100+km/h', | ||
151 | + }, | ||
152 | + levelMarks: { | ||
153 | + 0: '0m', | ||
154 | + 200: '200+m', | ||
155 | + }, | ||
156 | + minVerticalSpeed: 0, | ||
157 | + maxVerticalSpeed: 100, | ||
158 | + minHorizontalSpeed: 0, | ||
159 | + maxHorizontalSpeed: 100, | ||
160 | + minAboveSeaLevel: 0, | ||
161 | + maxAboveSeaLevel: 200, | ||
162 | + minAboveGroundLevel: 0, | ||
163 | + maxAboveGroundLevel: 200, | ||
164 | + }; | ||
165 | + }, | ||
166 | + computed: { | ||
167 | + ...mapGetters('Log/page', { | ||
168 | + getPageParams: 'getPageParams', | ||
169 | + }), | ||
170 | + }, | ||
171 | + created() { | ||
172 | + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams)); | ||
173 | + }, | ||
174 | + methods: { | ||
175 | + ...mapActions('Log/page', { | ||
176 | + setPageParams: 'setPageParams', | ||
177 | + }), | ||
178 | + searchData() { | ||
179 | + console.log(this.searchParams); | ||
180 | + this.setPageParams(this.searchParams); | ||
181 | + this.$emit('loadData'); | ||
182 | + }, | ||
183 | + onHorizontalSpeedChange(value) { | ||
184 | + this.minHorizontalSpeed = value[0]; | ||
185 | + this.maxHorizontalSpeed = value[1]; | ||
186 | + }, | ||
187 | + onHorizontalSpeedAfterChange(value) { | ||
188 | + this.hsOpenFlag = !this.hsOpenFlag; | ||
189 | + this.searchParams.minHorizontalSpeed = this.minHorizontalSpeed; | ||
190 | + this.searchParams.maxHorizontalSpeed = this.maxHorizontalSpeed; | ||
191 | + if (this.maxHorizontalSpeed == 100) { | ||
192 | + this.searchParams.maxHorizontalSpeed = null; | ||
193 | + } | ||
194 | + }, | ||
195 | + onVerticalSpeedChange(value) { | ||
196 | + this.minVerticalSpeed = value[0]; | ||
197 | + this.maxVerticalSpeed = value[1]; | ||
198 | + }, | ||
199 | + onVerticalSpeedAfterChange(value) { | ||
200 | + this.vsOpenFlag = !this.vsOpenFlag; | ||
201 | + this.searchParams.minVerticalSpeed = this.minVerticalSpeed; | ||
202 | + this.searchParams.maxVerticalSpeed = this.maxVerticalSpeed; | ||
203 | + if (this.maxVerticalSpeed == 100) { | ||
204 | + this.searchParams.maxVerticalSpeed = null; | ||
205 | + } | ||
206 | + }, | ||
207 | + onAboveSeaLevelChange(value) { | ||
208 | + this.minAboveSeaLevel = value[0]; | ||
209 | + this.maxAboveSeaLevel = value[1]; | ||
210 | + }, | ||
211 | + onAboveSeaLevelAfterChange(value) { | ||
212 | + this.aslOpenFlag = !this.aslOpenFlag; | ||
213 | + this.searchParams.minAboveSeaLevel = this.minAboveSeaLevel; | ||
214 | + this.searchParams.maxAboveSeaLevel = this.maxAboveSeaLevel; | ||
215 | + if (this.maxAboveSeaLevel == 200) { | ||
216 | + this.searchParams.maxAboveSeaLevel = null; | ||
217 | + } | ||
218 | + }, | ||
219 | + onAboveGroundLevelChange(value) { | ||
220 | + this.minAboveGroundLevel = value[0]; | ||
221 | + this.maxAboveGroundLevel = value[1]; | ||
222 | + }, | ||
223 | + onAboveGroundLevelAfterChange(value) { | ||
224 | + this.aglOpenFlag = !this.aglOpenFlag; | ||
225 | + this.searchParams.minAboveGroundLevel = this.minAboveGroundLevel; | ||
226 | + this.searchParams.maxAboveGroundLevel = this.maxAboveGroundLevel; | ||
227 | + if (this.maxAboveGroundLevel == 200) { | ||
228 | + this.searchParams.maxAboveGroundLevel = null; | ||
229 | + } | ||
230 | + }, | ||
231 | + }, | ||
232 | +}; | ||
233 | +</script> | ||
234 | + | ||
235 | +<style scoped lang="scss"> | ||
236 | +@import '@/assets/styles/mixins.scss'; | ||
237 | +.form-wrapper { | ||
238 | + //height: 40px; | ||
239 | + justify-content: space-between; | ||
240 | +} | ||
241 | +.form-wrapper, | ||
242 | +.form-item { | ||
243 | + display: flex; | ||
244 | + margin-right: 10px; | ||
245 | + margin-bottom: 0; | ||
246 | +} | ||
247 | + | ||
248 | +.slider-box { | ||
249 | + background: white; | ||
250 | + padding: 10px 23px; | ||
251 | + border-radius: 20px; | ||
252 | + border: solid 1px $antblue; | ||
253 | + position: fixed; | ||
254 | + z-index: 9; | ||
255 | +} | ||
256 | + | ||
257 | +.search-input-log { | ||
258 | + width: 100px; | ||
259 | +} | ||
260 | + | ||
261 | +.ant-btn { | ||
262 | + padding: 0 10px; | ||
263 | +} | ||
264 | +</style> |
1 | +<template> | ||
2 | + <a-table | ||
3 | + rowKey="id" | ||
4 | + bordered | ||
5 | + :loading="childLoading" | ||
6 | + :columns="columns" | ||
7 | + :data-source="getPageData" | ||
8 | + :scroll="{x: 1000}" | ||
9 | + :pagination="pagination" | ||
10 | + @change="changePage" | ||
11 | + > | ||
12 | + <a slot="modelName" slot-scope="data, row" @click="goDetail(row)"> | ||
13 | + {{ data }} | ||
14 | + </a> | ||
15 | + | ||
16 | + <div slot="horizontalSpeed" slot-scope="data">{{ data }} km/h</div> | ||
17 | + <div slot="verticalSpeed" slot-scope="data">{{ data }} km/h</div> | ||
18 | + <div slot="aboveGroundLevel" slot-scope="data">{{ data }} m</div> | ||
19 | + <div slot="aboveSeaLevel" slot-scope="data">{{ data }} m</div> | ||
20 | + </a-table> | ||
21 | +</template> | ||
22 | + | ||
23 | +<script> | ||
24 | +import databaseLogColumn from '@/utils/ColumnData/databaseLogColumn'; | ||
25 | +import { mapActions, mapGetters } from 'vuex'; | ||
26 | + | ||
27 | +export default { | ||
28 | + name: 'LogTable', | ||
29 | + props: { | ||
30 | + childLoading: { | ||
31 | + type: Boolean, | ||
32 | + default: false, | ||
33 | + }, | ||
34 | + }, | ||
35 | + data() { | ||
36 | + return { | ||
37 | + columns: databaseLogColumn, | ||
38 | + }; | ||
39 | + }, | ||
40 | + computed: { | ||
41 | + ...mapGetters('Log/page', { | ||
42 | + getPageData: 'getPageData', | ||
43 | + getPagination: 'getPagination', | ||
44 | + getPageParams: 'getPageParams', | ||
45 | + }), | ||
46 | + pagination: { | ||
47 | + get() { | ||
48 | + return this.getPagination; | ||
49 | + }, | ||
50 | + set(e) { | ||
51 | + this.setPagination({ | ||
52 | + size: e.size, | ||
53 | + page: e.page, | ||
54 | + }); | ||
55 | + }, | ||
56 | + }, | ||
57 | + }, | ||
58 | + methods: { | ||
59 | + ...mapActions('Log/page', { | ||
60 | + setPagination: 'setPagination', | ||
61 | + fetchPageData: 'fetchPageData', | ||
62 | + }), | ||
63 | + ...mapActions('Drone/detail', { | ||
64 | + fetchDroneDetailData: 'fetchDetailData', | ||
65 | + }), | ||
66 | + ...mapActions('Schedule/detail', { | ||
67 | + fetchScheduleDetailData: 'fetchDetailData', | ||
68 | + }), | ||
69 | + changePage(e) { | ||
70 | + this.pagination = { | ||
71 | + size: e.pageSize, | ||
72 | + page: e.current, | ||
73 | + }; | ||
74 | + this.fetchPageData(this.getPageParams); | ||
75 | + }, | ||
76 | + goDetail(row) { | ||
77 | + this.fetchDroneDetailData(row.droneId); | ||
78 | + this.fetchScheduleDetailData(row.scheduleId); | ||
79 | + this.$router.push({ | ||
80 | + path: `/database/schedule/${row.scheduleId}`, | ||
81 | + }); | ||
82 | + }, | ||
83 | + }, | ||
84 | +}; | ||
85 | +</script> | ||
86 | + | ||
87 | +<style scoped lang="scss"></style> |
1 | +<template> | ||
2 | + <a-page-header | ||
3 | + class="page-header" | ||
4 | + title="드론 정보" | ||
5 | + sub-title="Drone-Schedule-Log" | ||
6 | + @back="$router.go(-1)" | ||
7 | + > | ||
8 | + <div :style="{display: 'flex'}"> | ||
9 | + <div :style="{width: '30%'}"> | ||
10 | + <img :src="droneInfo.picture" :style="{width: '90%'}" /> | ||
11 | + </div> | ||
12 | + | ||
13 | + <div :style="{width: '70%', marginLeft: '20px'}"> | ||
14 | + <div class="label-modelName">{{ droneInfo.modelName }}</div> | ||
15 | + <div :style="{display: 'flex', marginBottom: '15px'}"> | ||
16 | + <div class="label-info">info</div> | ||
17 | + | ||
18 | + <a-descriptions | ||
19 | + :column="{xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1}" | ||
20 | + class="description-box" | ||
21 | + > | ||
22 | + <a-descriptions-item label="모델명"> | ||
23 | + {{ droneInfo.modelName }} | ||
24 | + </a-descriptions-item> | ||
25 | + <a-descriptions-item label="제조사"> | ||
26 | + {{ droneInfo.maker == null ? 'None' : droneInfo.maker }} | ||
27 | + </a-descriptions-item> | ||
28 | + <a-descriptions-item label="종류"> | ||
29 | + {{ droneInfo.usage == null ? 'None' : droneInfo.usageName }} | ||
30 | + </a-descriptions-item> | ||
31 | + <a-descriptions-item label="제원"> | ||
32 | + {{ | ||
33 | + droneInfo.specification == null | ||
34 | + ? 'None' | ||
35 | + : droneInfo.specification | ||
36 | + }} | ||
37 | + </a-descriptions-item> | ||
38 | + <a-descriptions-item label="무게"> | ||
39 | + {{ droneInfo.weight == null ? '?' : droneInfo.weight }} kg | ||
40 | + </a-descriptions-item> | ||
41 | + <a-descriptions-item label="No"> | ||
42 | + {{ droneInfo.id }} | ||
43 | + </a-descriptions-item> | ||
44 | + </a-descriptions> | ||
45 | + </div> | ||
46 | + <div :style="{display: 'flex'}"> | ||
47 | + <div class="label-info">Schedule</div> | ||
48 | + | ||
49 | + <a-descriptions | ||
50 | + :column="{xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1}" | ||
51 | + class="description-box" | ||
52 | + > | ||
53 | + <a-descriptions-item label="시작 시간"> | ||
54 | + {{ mc_dateTime(ScheduleInfo.startTime) }} | ||
55 | + </a-descriptions-item> | ||
56 | + <a-descriptions-item label="시작 위도"> | ||
57 | + {{ ScheduleInfo.startLatitude }} | ||
58 | + </a-descriptions-item> | ||
59 | + <a-descriptions-item label="시작 경도"> | ||
60 | + {{ ScheduleInfo.startLongitude }} | ||
61 | + </a-descriptions-item> | ||
62 | + <a-descriptions-item label="종료 시간"> | ||
63 | + {{ mc_dateTime(ScheduleInfo.terminateTime) }} | ||
64 | + </a-descriptions-item> | ||
65 | + <a-descriptions-item label="종료 위도"> | ||
66 | + {{ ScheduleInfo.terminateLatitude }} | ||
67 | + </a-descriptions-item> | ||
68 | + <a-descriptions-item label="종료 경도"> | ||
69 | + {{ ScheduleInfo.terminateLongitude }} | ||
70 | + </a-descriptions-item> | ||
71 | + </a-descriptions> | ||
72 | + </div> | ||
73 | + </div> | ||
74 | + </div> | ||
75 | + <a-divider /> | ||
76 | + </a-page-header> | ||
77 | +</template> | ||
78 | + | ||
79 | +<script> | ||
80 | +import {mapGetters} from 'vuex'; | ||
81 | + | ||
82 | +export default { | ||
83 | + name: 'DatabaseDetailHeader', | ||
84 | + components: {}, | ||
85 | + data() { | ||
86 | + return { | ||
87 | + droneInfo: {}, | ||
88 | + ScheduleInfo: {}, | ||
89 | + }; | ||
90 | + }, | ||
91 | + created() { | ||
92 | + this.droneInfo = this.getDetailData; | ||
93 | + this.ScheduleInfo = this.getScheduleDetailData; | ||
94 | + }, | ||
95 | + computed: { | ||
96 | + ...mapGetters('Drone/detail', { | ||
97 | + getDetailData: 'getDetailData', | ||
98 | + }), | ||
99 | + ...mapGetters('Schedule/detail', { | ||
100 | + getScheduleDetailData: 'getDetailData', | ||
101 | + }), | ||
102 | + ...mapGetters('Code', { | ||
103 | + getCodes: 'getCodes', | ||
104 | + }), | ||
105 | + droneCategory() { | ||
106 | + return (data) => { | ||
107 | + switch (parseInt(data, 10)) { | ||
108 | + case 1: | ||
109 | + return '촬영용'; | ||
110 | + case 2: | ||
111 | + return '레이싱용'; | ||
112 | + case 3: | ||
113 | + return '완구용'; | ||
114 | + default: | ||
115 | + return null; | ||
116 | + } | ||
117 | + }; | ||
118 | + }, | ||
119 | + }, | ||
120 | +}; | ||
121 | +</script> | ||
122 | + | ||
123 | +<style scoped lang="scss"> | ||
124 | +@import '@/assets/styles/mixins.scss'; | ||
125 | + | ||
126 | +.label-modelName { | ||
127 | + font-size: 30px; | ||
128 | + color: $antblue; | ||
129 | +} | ||
130 | +.label-info { | ||
131 | + padding: 0 10px; | ||
132 | + border-right: 1px solid #777777; | ||
133 | + min-width: 20%; | ||
134 | +} | ||
135 | +.description-box { | ||
136 | + padding-left: 10px; | ||
137 | +} | ||
138 | +</style> |
1 | +<template> | ||
2 | + <a-page-header | ||
3 | + class="page-header" | ||
4 | + title="Database" | ||
5 | + sub-title="Table - DroneSchedule" | ||
6 | + > | ||
7 | + <template slot="extra"> | ||
8 | + <a-button key="1" type="primary" form="form" html-type="submit"> | ||
9 | + 검색 | ||
10 | + </a-button> | ||
11 | + </template> | ||
12 | + | ||
13 | + <a-form id="form" @submit.prevent="searchData" class="form-wrapper"> | ||
14 | + <a-form-item label="검색 범위" class="form-item"> | ||
15 | + <div> | ||
16 | + <a-date-picker | ||
17 | + v-model="startValue" | ||
18 | + :disabled-date="disabledStartDate" | ||
19 | + show-time | ||
20 | + format="YYYY-MM-DD HH:mm:ss" | ||
21 | + placeholder="Start Date" | ||
22 | + @openChange="handleStartOpenChange" | ||
23 | + /> | ||
24 | + <span :style="{padding: '0 10px'}">-</span> | ||
25 | + <a-date-picker | ||
26 | + v-model="endValue" | ||
27 | + :disabled-date="disabledEndDate" | ||
28 | + show-time | ||
29 | + format="YYYY-MM-DD HH:mm:ss" | ||
30 | + placeholder="End Date" | ||
31 | + :open="endOpen" | ||
32 | + @openChange="handleEndOpenChange" | ||
33 | + /> | ||
34 | + </div> | ||
35 | + </a-form-item> | ||
36 | + </a-form> | ||
37 | + </a-page-header> | ||
38 | +</template> | ||
39 | + | ||
40 | +<script> | ||
41 | +import {mapActions, mapGetters} from 'vuex'; | ||
42 | + | ||
43 | +export default { | ||
44 | + name: 'DatabaseSearchFilter', | ||
45 | + components: {}, | ||
46 | + data() { | ||
47 | + return { | ||
48 | + searchParams: {}, | ||
49 | + startValue: null, | ||
50 | + endValue: null, | ||
51 | + endOpen: false, | ||
52 | + }; | ||
53 | + }, | ||
54 | + watch: { | ||
55 | + startValue(val) { | ||
56 | + if (val != null) { | ||
57 | + this.searchParams.startTime = val.format('YYYY-MM-DD HH:mm:ss'); | ||
58 | + } else { | ||
59 | + this.searchParams.startTime = null; | ||
60 | + } | ||
61 | + }, | ||
62 | + endValue(val) { | ||
63 | + if (val !== null) { | ||
64 | + this.searchParams.terminateTime = val.format('YYYY-MM-DD HH:mm:ss'); | ||
65 | + } else { | ||
66 | + this.searchParams.terminateTime = null; | ||
67 | + } | ||
68 | + }, | ||
69 | + }, | ||
70 | + computed: { | ||
71 | + ...mapGetters('Schedule/page', { | ||
72 | + getPageParams: 'getPageParams', | ||
73 | + }), | ||
74 | + }, | ||
75 | + created() { | ||
76 | + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams)); | ||
77 | + }, | ||
78 | + methods: { | ||
79 | + ...mapActions('Schedule/page', { | ||
80 | + setPageParams: 'setPageParams', | ||
81 | + }), | ||
82 | + searchData() { | ||
83 | + console.log(this.searchParams); | ||
84 | + this.setPageParams(this.searchParams); | ||
85 | + this.$emit('loadData'); | ||
86 | + }, | ||
87 | + disabledStartDate(startValue) { | ||
88 | + const endValue = this.endValue; | ||
89 | + if (!startValue || !endValue) { | ||
90 | + return false; | ||
91 | + } | ||
92 | + return startValue.valueOf() > endValue.valueOf(); | ||
93 | + }, | ||
94 | + disabledEndDate(endValue) { | ||
95 | + const startValue = this.startValue; | ||
96 | + if (!endValue || !startValue) { | ||
97 | + return false; | ||
98 | + } | ||
99 | + return startValue.valueOf() >= endValue.valueOf(); | ||
100 | + }, | ||
101 | + handleStartOpenChange(open) { | ||
102 | + if (!open) { | ||
103 | + this.endOpen = true; | ||
104 | + } | ||
105 | + }, | ||
106 | + handleEndOpenChange(open) { | ||
107 | + this.endOpen = open; | ||
108 | + }, | ||
109 | + }, | ||
110 | +}; | ||
111 | +</script> | ||
112 | + | ||
113 | +<style scoped lang="scss"> | ||
114 | +@import '@/assets/styles/mixins.scss'; | ||
115 | +.form-wrapper { | ||
116 | + height: 40px; | ||
117 | +} | ||
118 | +.form-wrapper, | ||
119 | +.form-item { | ||
120 | + display: flex; | ||
121 | + margin-left: 10px; | ||
122 | + margin-bottom: 0; | ||
123 | +} | ||
124 | +.slider-box { | ||
125 | + background: white; | ||
126 | + padding: 10px 15px; | ||
127 | + border-radius: 20px; | ||
128 | + border: solid 1px $antblue; | ||
129 | + position: fixed; | ||
130 | + z-index: 9; | ||
131 | +} | ||
132 | +</style> |
1 | +<template> | ||
2 | + <a-table | ||
3 | + rowKey="id" | ||
4 | + bordered | ||
5 | + :loading="childLoading" | ||
6 | + :columns="columns" | ||
7 | + :data-source="getPageData" | ||
8 | + :scroll="{x: 1000}" | ||
9 | + :pagination="pagination" | ||
10 | + @change="changePage" | ||
11 | + > | ||
12 | + <a slot="modelName" slot-scope="data, row" @click="goDetail(row)"> | ||
13 | + {{ data }} | ||
14 | + </a> | ||
15 | + <div slot="startTime" slot-scope="data"> | ||
16 | + {{ mc_dateTime(data) }} | ||
17 | + </div> | ||
18 | + <div slot="terminateTime" slot-scope="data"> | ||
19 | + {{ mc_dateTime(data) }} | ||
20 | + </div> | ||
21 | + </a-table> | ||
22 | +</template> | ||
23 | + | ||
24 | +<script> | ||
25 | +import databaseScheduleColumn from '@/utils/ColumnData/databaseScheduleColumn'; | ||
26 | +import {mapActions, mapGetters} from 'vuex'; | ||
27 | + | ||
28 | +export default { | ||
29 | + name: 'DatabaseTable', | ||
30 | + props: { | ||
31 | + childLoading: { | ||
32 | + type: Boolean, | ||
33 | + default: false, | ||
34 | + }, | ||
35 | + }, | ||
36 | + data() { | ||
37 | + return { | ||
38 | + columns: databaseScheduleColumn, | ||
39 | + }; | ||
40 | + }, | ||
41 | + computed: { | ||
42 | + ...mapGetters('Schedule/page', { | ||
43 | + getPageData: 'getPageData', | ||
44 | + getPagination: 'getPagination', | ||
45 | + getPageParams: 'getPageParams', | ||
46 | + }), | ||
47 | + pagination: { | ||
48 | + get() { | ||
49 | + return this.getPagination; | ||
50 | + }, | ||
51 | + set(e) { | ||
52 | + this.setPagination({ | ||
53 | + size: e.size, | ||
54 | + page: e.page, | ||
55 | + }); | ||
56 | + }, | ||
57 | + }, | ||
58 | + }, | ||
59 | + methods: { | ||
60 | + ...mapActions('Schedule/page', { | ||
61 | + setPagination: 'setPagination', | ||
62 | + fetchPageData: 'fetchPageData', | ||
63 | + }), | ||
64 | + ...mapActions('Drone/detail', { | ||
65 | + fetchDetailData: 'fetchDetailData', | ||
66 | + }), | ||
67 | + changePage(e) { | ||
68 | + this.pagination = { | ||
69 | + size: e.pageSize, | ||
70 | + page: e.current, | ||
71 | + }; | ||
72 | + this.fetchPageData(this.getPageParams); | ||
73 | + }, | ||
74 | + goDetail(row) { | ||
75 | + this.fetchDetailData(row.droneId); | ||
76 | + this.$router.push({ | ||
77 | + path: `/database/schedule/${row.id}`, | ||
78 | + }); | ||
79 | + }, | ||
80 | + }, | ||
81 | +}; | ||
82 | +</script> | ||
83 | + | ||
84 | +<style scoped lang="scss"></style> |
Frontend/src/components/Layout/footer.vue
0 → 100644
1 | +<template> | ||
2 | + <div> | ||
3 | + <h4 class="footer-font"> | ||
4 | + Drone Simulation Map | ||
5 | + </h4> | ||
6 | + <h5 class="footer-font"> | ||
7 | + @2021 TwentyOz & KHU | ||
8 | + </h5> | ||
9 | + </div> | ||
10 | +</template> | ||
11 | + | ||
12 | +<script> | ||
13 | +export default { | ||
14 | + name: 'LayoutFooter', | ||
15 | + data() { | ||
16 | + return { | ||
17 | + | ||
18 | + }; | ||
19 | + }, | ||
20 | + computed: { | ||
21 | + | ||
22 | + }, | ||
23 | + watch: { | ||
24 | + | ||
25 | + }, | ||
26 | + created() { | ||
27 | + | ||
28 | + }, | ||
29 | + method: { | ||
30 | + | ||
31 | + }, | ||
32 | +}; | ||
33 | +</script> | ||
34 | + | ||
35 | +<style scoped lang="scss"> | ||
36 | +@import '@/assets/styles/mixins.scss'; | ||
37 | + | ||
38 | +.footer-font { | ||
39 | + color: $gray-3 | ||
40 | +} | ||
41 | +</style> |
1 | +<template> | ||
2 | + <div> | ||
3 | + <div class="bottom-tool-box"> | ||
4 | + <div class="boxBtn" @click="clickWeatherBtn"> | ||
5 | + <a-icon type="cloud"/> | ||
6 | + </div> | ||
7 | + <FilterBtnBox | ||
8 | + class="filterBox" | ||
9 | + @clickClose="clickFilterBoxClose" | ||
10 | + @changeFilterMode="e => this.$emit('changeFilterMode', e)" | ||
11 | + @changeSearchMode="e => this.$emit('changeSearchMode', e)" | ||
12 | + /> | ||
13 | + <div class="boxBtn" @click="clickBookMarkBtn"> | ||
14 | + <a-icon type="star"/> | ||
15 | + </div> | ||
16 | + </div> | ||
17 | + </div> | ||
18 | +</template> | ||
19 | +<script> | ||
20 | +import FilterBtnBox from '../FilterBox/filterBtnBox'; | ||
21 | + | ||
22 | +export default { | ||
23 | + head() { | ||
24 | + return { | ||
25 | + title: 'Drone', | ||
26 | + meta: [ | ||
27 | + { | ||
28 | + hid: 'database', | ||
29 | + name: 'Descriptions', | ||
30 | + content: 'DroneWeb-Content', | ||
31 | + }, | ||
32 | + ], | ||
33 | + }; | ||
34 | + }, | ||
35 | + components: { | ||
36 | + FilterBtnBox | ||
37 | + }, | ||
38 | + props: { | ||
39 | + }, | ||
40 | + data() { | ||
41 | + return { | ||
42 | + searchMode: false, | ||
43 | + filterMode: false, | ||
44 | + }; | ||
45 | + }, | ||
46 | + computed: { | ||
47 | + }, | ||
48 | + created() { | ||
49 | + }, | ||
50 | + methods: { | ||
51 | + clickWeatherBtn() { | ||
52 | + console.log('click') | ||
53 | + this.$notification['warning']({ | ||
54 | + message: '날씨 기능은 추후 추가될 예정입니다.', | ||
55 | + duration: 3, | ||
56 | + }) | ||
57 | + }, | ||
58 | + clickBookMarkBtn() { | ||
59 | + this.$notification['warning']({ | ||
60 | + message: '즐겨찾기 기능은 추후 추가될 예정입니다.', | ||
61 | + duration: 3, | ||
62 | + }) | ||
63 | + }, | ||
64 | + toggleFilterMode() { | ||
65 | + this.$emit('toggleFilterMode') | ||
66 | + }, | ||
67 | + toggleSearchMode() { | ||
68 | + this.$emit('toggleSearchMode') | ||
69 | + }, | ||
70 | + clickSearchBoxClose() { | ||
71 | + this.searchMode = false; | ||
72 | + }, | ||
73 | + clickFilterBoxClose() { | ||
74 | + this.filterMode = false; | ||
75 | + }, | ||
76 | + clickSearchBtn() { | ||
77 | + this.filterMode = false; | ||
78 | + this.searchMode = true; | ||
79 | + }, | ||
80 | + clickFilterBtn() { | ||
81 | + this.searchMode = false; | ||
82 | + this.filterMode = true; | ||
83 | + }, | ||
84 | + | ||
85 | + }, | ||
86 | +}; | ||
87 | +</script> | ||
88 | +<style lang="scss"> | ||
89 | +</style> |
1 | +<template> | ||
2 | + <div> | ||
3 | + <a-descriptions layout="vertical" bordered> | ||
4 | + <a-descriptions-item :span="4"> | ||
5 | + <template v-slot:label> | ||
6 | + <div>{{ foundDrone.modelName }}</div> | ||
7 | + </template> | ||
8 | + <img | ||
9 | + :src="foundDrone.picture || require('@/assets/images/drone-image.jpg')" | ||
10 | + :style="{width: '300px'}" | ||
11 | + /> | ||
12 | + </a-descriptions-item> | ||
13 | + <a-descriptions-item label="실시간 정보" :span="4"> | ||
14 | + <div class="des-sub-title">현재 위치</div> | ||
15 | + <div class="des-sub-cont-grid-4" style="margin-bottom: 10px;"> | ||
16 | + <div> | ||
17 | + <div>경도</div> | ||
18 | + <div>{{ selectedLastLog.longitude ? selectedLastLog.longitude.toFixed(3) : '?' }}</div> | ||
19 | + </div> | ||
20 | + <div> | ||
21 | + <div>위도</div> | ||
22 | + <div>{{ selectedLastLog.latitude ? selectedLastLog.latitude.toFixed(3) : '?' }}</div> | ||
23 | + </div> | ||
24 | + <div> | ||
25 | + <div>이동거리</div> | ||
26 | + <div>{{ Math.floor(getDetailData.distance + selectedLastLog.distance) }}m</div> | ||
27 | + </div> | ||
28 | + <div> | ||
29 | + <div>운용시간</div> | ||
30 | + <div>{{ getTimeDiff(foundSchedule.startTime) }}</div> | ||
31 | + </div> | ||
32 | + </div> | ||
33 | + <div class="des-sub-cont-grid-2" style="margin-bottom: 10px;"> | ||
34 | + <div> | ||
35 | + <div class="des-sub-title">현재 속도</div> | ||
36 | + <div class="des-sub-cont-grid-2"> | ||
37 | + <div> | ||
38 | + <div>수평 속도</div> | ||
39 | + <div>{{ selectedLastLog.horizontalSpeed }}km/h</div> | ||
40 | + </div> | ||
41 | + <div> | ||
42 | + <div>수직 속도</div> | ||
43 | + <div>{{ selectedLastLog.verticalSpeed }}km/h</div> | ||
44 | + </div> | ||
45 | + </div> | ||
46 | + </div> | ||
47 | + <div> | ||
48 | + <div class="des-sub-title">현재 고도</div> | ||
49 | + <div class="des-sub-cont-grid-2"> | ||
50 | + <div> | ||
51 | + <div>지면고도</div> | ||
52 | + <div>{{ selectedLastLog.aboveGroundLevel }}m</div> | ||
53 | + </div> | ||
54 | + <div> | ||
55 | + <div>해발고도</div> | ||
56 | + <div>{{ selectedLastLog.aboveSeaLevel }}m</div> | ||
57 | + </div> | ||
58 | + </div> | ||
59 | + </div> | ||
60 | + </div> | ||
61 | + </a-descriptions-item> | ||
62 | + <a-descriptions-item label="스케쥴" :span="4" :style="{padding: '0px'}"> | ||
63 | + <div class="des-sub-cont-grid-2" style="margin-bottom: 10px;"> | ||
64 | + <div> | ||
65 | + <div class="des-sub-title">시작 스케쥴</div> | ||
66 | + <div> | ||
67 | + <div>날짜/시간</div> | ||
68 | + <div style="margin-bottom: 5px;">{{ mc_dateTime(foundSchedule.startTime) || '?' }}</div> | ||
69 | + <div>경도</div> | ||
70 | + <div style="margin-bottom: 5px;">{{ foundSchedule.startLongitude || '?' }}</div> | ||
71 | + <div>위도</div> | ||
72 | + <div style="margin-bottom: 5px;">{{ foundSchedule.startLatitude || '?' }}</div> | ||
73 | + </div> | ||
74 | + </div> | ||
75 | + <div> | ||
76 | + <div class="des-sub-title">실제 시작</div> | ||
77 | + <div> | ||
78 | + <div>날짜/시간</div> | ||
79 | + <div style="margin-bottom: 5px;">{{ droneLogs.length !== 0 ? mc_dateTime(droneLogs[0].createdAt) : "?" }}</div> | ||
80 | + <div>경도</div> | ||
81 | + <div style="margin-bottom: 5px;">{{ droneLogs.length !== 0 ? droneLogs[0].longitude : "?" }}</div> | ||
82 | + <div>위도</div> | ||
83 | + <div style="margin-bottom: 5px;">{{ droneLogs.length !== 0 ? droneLogs[0].latitude : "?" }}</div> | ||
84 | + </div> | ||
85 | + </div> | ||
86 | + </div> | ||
87 | + <div class="des-sub-title">종료 스케쥴</div> | ||
88 | + <div style="display: grid; grid-template-columns: 2fr 1fr 1fr"> | ||
89 | + <div> | ||
90 | + <div>날짜/시간</div> | ||
91 | + <div>{{ mc_dateTime(foundSchedule.terminateTime) || '?' }}</div> | ||
92 | + </div> | ||
93 | + <div> | ||
94 | + <div>경도</div> | ||
95 | + <div>{{ foundSchedule.terminateLongitude || '?' }}</div> | ||
96 | + </div> | ||
97 | + <div> | ||
98 | + <div>위도</div> | ||
99 | + <div>{{ foundSchedule.terminateLatitude || '?' }}</div> | ||
100 | + </div> | ||
101 | + </div> | ||
102 | + </a-descriptions-item> | ||
103 | + <a-descriptions-item label="드론 정보" :span="6"> | ||
104 | + <div class="des-sub-cont-grid-4"> | ||
105 | + <div> | ||
106 | + <div class="des-sub-title">제조사</div> | ||
107 | + <div>{{ foundDrone.maker || '?' }}</div> | ||
108 | + </div> | ||
109 | + <div> | ||
110 | + <div class="des-sub-title">종류</div> | ||
111 | + <div>{{ foundDrone.usageName || '?' }}</div> | ||
112 | + </div> | ||
113 | + <div> | ||
114 | + <div class="des-sub-title">제원</div> | ||
115 | + <div>{{ foundDrone.specification || '?' }}</div> | ||
116 | + </div> | ||
117 | + <div> | ||
118 | + <div class="des-sub-title">무게</div> | ||
119 | + <div>{{ foundDrone.weight }}g</div> | ||
120 | + </div> | ||
121 | + </div> | ||
122 | + </a-descriptions-item> | ||
123 | + </a-descriptions> | ||
124 | + </div> | ||
125 | +</template> | ||
126 | + | ||
127 | +<script> | ||
128 | +import { mapActions, mapGetters } from 'vuex'; | ||
129 | + | ||
130 | +export default { | ||
131 | + head() { | ||
132 | + return { | ||
133 | + title: 'Drone', | ||
134 | + meta: [ | ||
135 | + { | ||
136 | + hid: 'database', | ||
137 | + name: 'Descriptions', | ||
138 | + content: 'DroneWeb-Content', | ||
139 | + }, | ||
140 | + ], | ||
141 | + }; | ||
142 | + }, | ||
143 | + watch: { | ||
144 | + getSelectedLastLog: { | ||
145 | + deep: true, | ||
146 | + handler(newVal) { | ||
147 | + this.selectedLastLog = newVal; | ||
148 | + }, | ||
149 | + }, | ||
150 | + }, | ||
151 | + data() { | ||
152 | + return { | ||
153 | + selectedLastLog: {}, | ||
154 | + }; | ||
155 | + }, | ||
156 | + computed: { | ||
157 | + ...mapGetters('Drone/drone', { | ||
158 | + getDetailData: 'getDetailData', | ||
159 | + getSelectedLastLog: 'getSelectedLastLog', | ||
160 | + }), | ||
161 | + foundSchedule() { | ||
162 | + return this.getDetailData.foundSchedule || {}; | ||
163 | + }, | ||
164 | + foundDrone() { | ||
165 | + return this.getDetailData.foundDrone || {}; | ||
166 | + }, | ||
167 | + droneLogs() { | ||
168 | + return this.getDetailData.droneLogs || []; | ||
169 | + }, | ||
170 | + selectedLastLog() { | ||
171 | + return this.getSelectedLastLog || {}; | ||
172 | + }, | ||
173 | + }, | ||
174 | + methods: { | ||
175 | + getTimeDiff(startTime) { | ||
176 | + const totalSeconds = this.$dayjs().diff(this.$dayjs(startTime), 's'); | ||
177 | + const seconds = totalSeconds % 60; | ||
178 | + const minutes = Math.floor(totalSeconds / 60) % 60; | ||
179 | + const hours = Math.floor(totalSeconds / 3600); | ||
180 | + | ||
181 | + return `${hours}:${minutes}:${seconds}`; | ||
182 | + }, | ||
183 | + }, | ||
184 | +}; | ||
185 | +</script> | ||
186 | +<style lang="scss"> | ||
187 | +.des-sub-title { | ||
188 | + font-size: 16px; | ||
189 | + font-weight: 900; | ||
190 | +} | ||
191 | + | ||
192 | +.des-sub-cont-grid-2 { | ||
193 | + display: grid; | ||
194 | + grid-template-columns: 1fr 1fr; | ||
195 | +} | ||
196 | + | ||
197 | +.des-sub-cont-grid-4 { | ||
198 | + display: grid; | ||
199 | + grid-template-columns: 1fr 1fr 1fr 1fr; | ||
200 | +} | ||
201 | + | ||
202 | +.des-sub-cont { | ||
203 | + display: flex; | ||
204 | + gap: 20px; | ||
205 | + margin-bottom: 20px; | ||
206 | +} | ||
207 | + | ||
208 | +</style> |
1 | +<template> | ||
2 | + <div> | ||
3 | + <img | ||
4 | + @click="clickFilterBtn" | ||
5 | + class="filterBtn" | ||
6 | + :src="require('@/static/img/filter.png')"/> | ||
7 | + </div> | ||
8 | +</template> | ||
9 | +<script> | ||
10 | + | ||
11 | +import CloseBox from '@/components/_Common/CloseBox/closeBox'; | ||
12 | +import { mapActions, mapGetters } from 'vuex'; | ||
13 | + | ||
14 | +export default { | ||
15 | + head() { | ||
16 | + return { | ||
17 | + title: 'Drone', | ||
18 | + meta: [ | ||
19 | + { | ||
20 | + hid: 'database', | ||
21 | + name: 'Descriptions', | ||
22 | + content: 'DroneWeb-Content', | ||
23 | + }, | ||
24 | + ], | ||
25 | + }; | ||
26 | + }, | ||
27 | + components: { | ||
28 | + CloseBox, | ||
29 | + }, | ||
30 | + props: {}, | ||
31 | + data() { | ||
32 | + return { | ||
33 | + filterMode: false, | ||
34 | + filteredDroneList: [], | ||
35 | + manufacturerValue: [], | ||
36 | + weightValue: [0, 50], | ||
37 | + weightMarks: { | ||
38 | + 0: '0kg', | ||
39 | + 50: '50kg+', | ||
40 | + }, | ||
41 | + altitudeValue: [0, 200], | ||
42 | + altitudeMarks: { | ||
43 | + 0: '0m', | ||
44 | + 200: '200m+', | ||
45 | + }, | ||
46 | + speedValue: [0, 100], | ||
47 | + speedMarks: { | ||
48 | + 0: '0km/h', | ||
49 | + 100: '100km/h+', | ||
50 | + }, | ||
51 | + filmingOptions: ['True', 'False'], | ||
52 | + filmingValue: 'True', | ||
53 | + }; | ||
54 | + }, | ||
55 | + computed: { | ||
56 | + ...mapGetters('Etc', { | ||
57 | + getMakers: 'getMakers', | ||
58 | + }), | ||
59 | + ...mapGetters('Drone', { | ||
60 | + getLogFilter: 'drone/getLogFilter', | ||
61 | + getFixedDroneList: 'list/getFixedDroneList', | ||
62 | + }), | ||
63 | + }, | ||
64 | + created() { | ||
65 | + }, | ||
66 | + methods: { | ||
67 | + ...mapActions('Drone/drone', { | ||
68 | + setLogFilter: 'setLogFilter', | ||
69 | + clearLogFilter: 'clearLogFilter', | ||
70 | + }), | ||
71 | + clickFilterBtn() { | ||
72 | + this.$emit('changeSearchMode', false) | ||
73 | + this.$emit('changeFilterMode', true) | ||
74 | + }, | ||
75 | + clickClose() { | ||
76 | + this.$emit('clickClose'); | ||
77 | + }, | ||
78 | + changeManufacturer(value) { | ||
79 | + this.manufacturerValue = value; | ||
80 | + this.filteredDroneList = this.getFixedDroneList.filter((v) => !!this.manufacturerValue.find((e) => v.maker === e)); | ||
81 | + }, | ||
82 | + changeWeight(weight) { | ||
83 | + this.weightValue = weight; | ||
84 | + }, | ||
85 | + changeAltitude(altitude) { | ||
86 | + this.altitudeValue = altitude; | ||
87 | + }, | ||
88 | + changeSpeed(speed) { | ||
89 | + this.speedValue = speed; | ||
90 | + }, | ||
91 | + applyFilter() { | ||
92 | + this.setLogFilter({ | ||
93 | + checkFilter: true, | ||
94 | + maker: this.manufacturerValue, | ||
95 | + filteredDroneList: this.filteredDroneList, | ||
96 | + weight: this.weightValue, | ||
97 | + altitude: this.altitudeValue, | ||
98 | + speed: this.speedValue, | ||
99 | + filming: this.filmingValue, | ||
100 | + }); | ||
101 | + }, | ||
102 | + clickReset() { | ||
103 | + this.clearLogFilter(); | ||
104 | + this.filteredDroneList = []; | ||
105 | + this.manufacturerValue = []; | ||
106 | + this.weightValue = [0, 50]; | ||
107 | + this.altitudeValue = [0, 200]; | ||
108 | + this.speedValue = [0, 100]; | ||
109 | + this.filmingValue = []; | ||
110 | + }, | ||
111 | + }, | ||
112 | +}; | ||
113 | +</script> | ||
114 | +<style lang="scss"> | ||
115 | +</style> |
1 | +<template> | ||
2 | + <div> | ||
3 | + <CloseBox | ||
4 | + @clickClose="clickClose" | ||
5 | + > | ||
6 | + <template v-slot:header> | ||
7 | + 드론 필터 | ||
8 | + </template> | ||
9 | + <template v-slot:body> | ||
10 | + <a-alert | ||
11 | + v-if="getLogFilter.checkFilter" | ||
12 | + message="필터가 적용된 상태입니다." | ||
13 | + type="info" show-icon | ||
14 | + class="filter-alert" | ||
15 | + /> | ||
16 | + <div :style="{padding: '0 10px 0 5px', marginTop: getLogFilter.checkFilter ? '-5px' : '-20px'}"> | ||
17 | + <div class="label"> | ||
18 | + <span>제조사</span> | ||
19 | + </div> | ||
20 | + <a-select | ||
21 | + :value="manufacturerValue" | ||
22 | + mode="tags" | ||
23 | + style="width: 100%;" | ||
24 | + placeholder="제조사" | ||
25 | + @change="changeManufacturer"> | ||
26 | + | ||
27 | + <a-select-option v-for="(maker,index) in getMakers" | ||
28 | + :key="index" | ||
29 | + :value="maker" | ||
30 | + > | ||
31 | + {{ maker }} | ||
32 | + </a-select-option> | ||
33 | + </a-select> | ||
34 | + <div class="label"><span>무게</span></div> | ||
35 | + <a-slider | ||
36 | + :value="weightValue" | ||
37 | + range | ||
38 | + :marks="weightMarks" | ||
39 | + :max="50" | ||
40 | + :min="0" | ||
41 | + @change="changeWeight" | ||
42 | + /> | ||
43 | + <div class="label"><span>고도</span></div> | ||
44 | + <a-slider | ||
45 | + :value="altitudeValue" | ||
46 | + range | ||
47 | + :marks="altitudeMarks" | ||
48 | + :max="200" | ||
49 | + :min="0" | ||
50 | + @change="changeAltitude"/> | ||
51 | + <div class="label"><span>속력</span></div> | ||
52 | + <a-slider | ||
53 | + :value="speedValue" | ||
54 | + range | ||
55 | + :marks="speedMarks" | ||
56 | + :max="100" | ||
57 | + :min="0" | ||
58 | + @change="changeSpeed"/> | ||
59 | + | ||
60 | + <div style="display: flex; justify-content: space-between; text-align: right; margin-top: 40px"> | ||
61 | + <a-button @click="applyFilter"> | ||
62 | + <a-icon type="check"/> | ||
63 | + 필터 적용 | ||
64 | + </a-button> | ||
65 | + <a-button @click="clickReset"> | ||
66 | + <a-icon type="reload"/> | ||
67 | + 필터 리셋 | ||
68 | + </a-button> | ||
69 | + </div> | ||
70 | + </div> | ||
71 | + </template> | ||
72 | + </CloseBox> | ||
73 | + </div> | ||
74 | +</template> | ||
75 | +<script> | ||
76 | + | ||
77 | +import CloseBox from '@/components/_Common/CloseBox/closeBox'; | ||
78 | +import { mapActions, mapGetters } from 'vuex'; | ||
79 | + | ||
80 | +export default { | ||
81 | + head() { | ||
82 | + return { | ||
83 | + title: 'Drone', | ||
84 | + meta: [ | ||
85 | + { | ||
86 | + hid: 'database', | ||
87 | + name: 'Descriptions', | ||
88 | + content: 'DroneWeb-Content', | ||
89 | + }, | ||
90 | + ], | ||
91 | + }; | ||
92 | + }, | ||
93 | + components: { | ||
94 | + CloseBox, | ||
95 | + }, | ||
96 | + props: {}, | ||
97 | + data() { | ||
98 | + return { | ||
99 | + filteredDroneList: [], | ||
100 | + manufacturerValue: [], | ||
101 | + weightValue: [0, 50], | ||
102 | + weightMarks: { | ||
103 | + 0: '0kg', | ||
104 | + 50: '50kg+', | ||
105 | + }, | ||
106 | + altitudeValue: [0, 200], | ||
107 | + altitudeMarks: { | ||
108 | + 0: '0m', | ||
109 | + 200: '200m+', | ||
110 | + }, | ||
111 | + speedValue: [0, 100], | ||
112 | + speedMarks: { | ||
113 | + 0: '0km/h', | ||
114 | + 100: '100km/h+', | ||
115 | + }, | ||
116 | + filmingOptions: ['True', 'False'], | ||
117 | + filmingValue: 'True', | ||
118 | + }; | ||
119 | + }, | ||
120 | + computed: { | ||
121 | + ...mapGetters('Etc', { | ||
122 | + getMakers: 'getMakers', | ||
123 | + }), | ||
124 | + ...mapGetters('Drone', { | ||
125 | + getLogFilter: 'drone/getLogFilter', | ||
126 | + getFixedDroneList: 'list/getFixedDroneList', | ||
127 | + }), | ||
128 | + }, | ||
129 | + created() { | ||
130 | + }, | ||
131 | + methods: { | ||
132 | + ...mapActions('Drone/drone', { | ||
133 | + setLogFilter: 'setLogFilter', | ||
134 | + clearLogFilter: 'clearLogFilter', | ||
135 | + }), | ||
136 | + clickClose() { | ||
137 | + this.$emit('clickClose'); | ||
138 | + }, | ||
139 | + changeManufacturer(value) { | ||
140 | + this.manufacturerValue = value; | ||
141 | + this.filteredDroneList = this.getFixedDroneList.filter((v) => !!this.manufacturerValue.find((e) => v.maker === e)); | ||
142 | + }, | ||
143 | + changeWeight(weight) { | ||
144 | + this.weightValue = weight; | ||
145 | + }, | ||
146 | + changeAltitude(altitude) { | ||
147 | + this.altitudeValue = altitude; | ||
148 | + }, | ||
149 | + changeSpeed(speed) { | ||
150 | + this.speedValue = speed; | ||
151 | + }, | ||
152 | + applyFilter() { | ||
153 | + this.setLogFilter({ | ||
154 | + checkFilter: true, | ||
155 | + maker: this.manufacturerValue, | ||
156 | + filteredDroneList: this.filteredDroneList, | ||
157 | + weight: this.weightValue, | ||
158 | + altitude: this.altitudeValue, | ||
159 | + speed: this.speedValue, | ||
160 | + filming: this.filmingValue, | ||
161 | + }); | ||
162 | + }, | ||
163 | + clickReset() { | ||
164 | + this.clearLogFilter(); | ||
165 | + this.filteredDroneList = []; | ||
166 | + this.manufacturerValue = []; | ||
167 | + this.weightValue = [0, 50]; | ||
168 | + this.altitudeValue = [0, 200]; | ||
169 | + this.speedValue = [0, 100]; | ||
170 | + this.filmingValue = []; | ||
171 | + }, | ||
172 | + }, | ||
173 | +}; | ||
174 | +</script> | ||
175 | +<style lang="scss"> | ||
176 | +</style> |
1 | +<template> | ||
2 | + <div> | ||
3 | + <img | ||
4 | + class="searchBtn" | ||
5 | + @click="clickSearchBtn" | ||
6 | + :src="require('@/static/img/search.png')"/> | ||
7 | + </div> | ||
8 | +</template> | ||
9 | +<script> | ||
10 | + | ||
11 | +import CloseBox from '@/components/_Common/CloseBox/closeBox'; | ||
12 | +import { mapGetters } from 'vuex'; | ||
13 | + | ||
14 | +export default { | ||
15 | + head() { | ||
16 | + return { | ||
17 | + title: 'Drone', | ||
18 | + meta: [ | ||
19 | + { | ||
20 | + hid: 'database', | ||
21 | + name: 'Descriptions', | ||
22 | + content: 'DroneWeb-Content', | ||
23 | + }, | ||
24 | + ], | ||
25 | + }; | ||
26 | + }, | ||
27 | + components: { | ||
28 | + CloseBox, | ||
29 | + }, | ||
30 | + props: {}, | ||
31 | + data() { | ||
32 | + return { | ||
33 | + searchValue: null, | ||
34 | + searchType: 'modelName', | ||
35 | + searchData: [], | ||
36 | + dataSource: [], | ||
37 | + searchMode: false, | ||
38 | + }; | ||
39 | + }, | ||
40 | + computed: { | ||
41 | + ...mapGetters('Etc', { | ||
42 | + getMakerDroneList: 'getMakerDroneList', | ||
43 | + }), | ||
44 | + ...mapGetters('Drone', { | ||
45 | + getLogFilter: 'drone/getLogFilter', | ||
46 | + getDroneLogs: 'drone/getDroneLogs', | ||
47 | + }), | ||
48 | + }, | ||
49 | + created() { | ||
50 | + }, | ||
51 | + methods: { | ||
52 | + async onSearch(value) { | ||
53 | + const params = {}; | ||
54 | + params[this.searchType] = value; | ||
55 | + const result = await this.$store.dispatch('Drone/list/fetchListContents', params); | ||
56 | + const objectDrone = {}; | ||
57 | + this.getDroneLogs.forEach((e) => { | ||
58 | + objectDrone[Number(e.droneId)] = true; | ||
59 | + }); | ||
60 | + this.searchData = [...result.drones.filter((v) => objectDrone[Number(v.id)])]; | ||
61 | + }, | ||
62 | + clickSearchBtn() { | ||
63 | + this.$emit('changeFilterMode', false) | ||
64 | + this.$emit('changeSearchMode', true) | ||
65 | + }, | ||
66 | + clickClose() { | ||
67 | + this.$emit('clickClose'); | ||
68 | + this.searchMode = false; | ||
69 | + }, | ||
70 | + clickListItem(item) { | ||
71 | + console.log('click', item); | ||
72 | + }, | ||
73 | + }, | ||
74 | +}; | ||
75 | +</script> | ||
76 | +<style lang="scss"> | ||
77 | +</style> |
This diff is collapsed. Click to expand it.
Frontend/src/components/Main/drone.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/components/Main/header.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/components/README.md
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Frontend/src/components/_Common/README.md
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/components/logo.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/layouts/README.md
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/layouts/auth.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/layouts/default.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/layouts/error.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/middleware/README.md
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/middleware/router.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/modules/vuelayers.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/README.md
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/analytics/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/auth/403.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/auth/404.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/auth/500.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/database/drone/_id.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/database/drone/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/database/log/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/database/schedule/_id.vue
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Frontend/src/pages/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/indexVL.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/pages/websocket/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/plugins/ApiClient/index.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/plugins/Dayjs/index.js
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Frontend/src/plugins/README.md
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/plugins/antDesign.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/plugins/client-only.client.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/plugins/globalMixins.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/plugins/highcharts.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/plugins/vuelayers.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/static/README.md
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/static/favicon.ico
0 → 100644
No preview for this file type
Frontend/src/static/img/drone.jpeg
0 → 100644
76.2 KB
Frontend/src/static/img/drone.png
0 → 100644
80.2 KB
Frontend/src/static/img/drone.svg
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/static/img/droneImage.jpeg
0 → 100644
1.04 MB
Frontend/src/static/img/filter.png
0 → 100644
4.1 KB
Frontend/src/static/img/gps.png
0 → 100644
13.2 KB
Frontend/src/static/img/search.png
0 → 100644
8.27 KB
Frontend/src/store/Code/index.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Drone/Map/index.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Drone/detail.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Drone/drone.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Drone/list.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Drone/page.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Etc/index.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Log/detail.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Log/list.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Log/page.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Member/detail.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Member/list.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Member/page.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/README.md
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Schedule/detail.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Schedule/list.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/Schedule/page.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/src/store/index.js
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Frontend/src/utils/Mixins/index.js
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/test/websocket-load-test.yaml
0 → 100644
This diff is collapsed. Click to expand it.
Frontend/yarn.lock
0 → 100644
This diff could not be displayed because it is too large.
-
Please register or login to post a comment