main func

This commit is contained in:
grey-cat-1908 2024-03-23 20:40:37 +03:00
parent 477d650acb
commit 02733bc884
9 changed files with 415 additions and 99 deletions

View file

@ -3,8 +3,8 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title> <title>Metro Map Maker</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View file

@ -9,6 +9,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@mdi/font": "^7.4.47",
"@svgdotjs/svg.js": "^3.2.0", "@svgdotjs/svg.js": "^3.2.0",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
@ -17,6 +18,7 @@
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.5.2", "@vitejs/plugin-vue": "^4.5.2",
"material-design-icons-iconfont": "^6.7.0",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.0.8", "vite": "^5.0.8",
"vue-tsc": "^1.8.25" "vue-tsc": "^1.8.25"

View file

@ -8,56 +8,89 @@ import {Station} from "./models/map/station.ts";
import {ConnectedStation} from "./models/map/connectedStation.ts"; import {ConnectedStation} from "./models/map/connectedStation.ts";
import {ExtraBranch} from "./models/map/extraBranch.ts"; import {ExtraBranch} from "./models/map/extraBranch.ts";
import {set, useEventListener} from '@vueuse/core' import {useEventListener} from '@vueuse/core'
import {Settings} from "./models/settings.ts"; import {Settings} from "./models/settings.ts";
import {defineStore} from "pinia";
const element = ref<HTMLDivElement>() const element = ref<HTMLDivElement>()
const xcord = ref(0); const xcord = ref(0);
const ycord = ref(0); const ycord = ref(0);
const mapWidth = ref(window.innerWidth);
const mapHeight = ref(window.innerHeight / 2);
useEventListener(element, 'click', (evt) => { useEventListener(element, 'click', (evt) => {
xcord.value = evt.offsetX; xcord.value = evt.offsetX;
ycord.value = evt.offsetY; ycord.value = evt.offsetY;
}) })
import StationsTable from "./components/StationsTable.vue";
import NewStation from "./components/NewStation.vue";
import BaseSettings from "./components/BaseSettings.vue";
import {useStore} from './utils/store.ts';
const store = useStore()
let branch: Branch = new Branch('','', -1, []) let branch: Branch = new Branch('','', -1, [])
let settings: Settings = new Settings(mapWidth.value, mapHeight.value); let settings: Settings = new Settings(store.width, store.height);
let map: Map; let map: Map;
const useStore = defineStore('branch', {
state: () => {
return {
name: 'Тест',
number: 1,
color: '#0078BE',
stations: []
}
},
})
// @ts-ignore
const store = useStore()
onMounted(() => { onMounted(() => {
store.stations = [
{'name': 'Начало', 'up': true, 'connectedStations': [], 'step': 0},
{'name': 'Конец', 'up': true, 'connectedStations': [], 'step': store.width - 400}
]
map = new Map(branch, settings); map = new Map(branch, settings);
build(); build();
}) })
function importFile() {
let element = document.getElementById('fileInput');
element.click();
}
function setFile(event) {
const reader = new FileReader();
reader.addEventListener('load', (readEvent) => {
if (typeof readEvent.target.result === "string") {
let importedStore = JSON.parse(readEvent.target.result);
store.name = importedStore.name;
store.width = importedStore.width;
store.height = importedStore.height;
store.color = importedStore.color;
store.number = importedStore.number;
store.stations = importedStore.stations;
build();
}
});
reader.readAsText(event.target.files[0]);
}
function save () {
let text = JSON.stringify(store);
let filename = 'metroMap.json';
let element = document.createElement('a');
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
function build () { function build () {
let stations = []; let stations = [];
store.stations = store.stations.sort((a,b) => a.step - b.step);
for (let station of store.stations) { for (let station of store.stations) {
let connectedStations = []; let connectedStations = [];
for (let connected in station['connectedStations']) { for (let connected of station['connectedStations']) {
let extraBranch: ExtraBranch = new ExtraBranch(connected['branch']['color'], connected['branch']['number']); let extraBranch: ExtraBranch = new ExtraBranch(connected['branch']['color'], connected['branch']['number']);
let extraStation: ConnectedStation = new ConnectedStation(connected['name'], extraBranch); let extraStation: ConnectedStation = new ConnectedStation(connected['name'], extraBranch);
@ -70,7 +103,7 @@ function build () {
} }
let branch: Branch = new Branch(store.name,store.color, store.number, stations) let branch: Branch = new Branch(store.name,store.color, store.number, stations)
let settings: Settings = new Settings(mapWidth.value, mapHeight.value); let settings: Settings = new Settings(store.width, store.height);
map.setSettings(settings) map.setSettings(settings)
map.setBranch(branch) map.setBranch(branch)
@ -88,90 +121,25 @@ function build () {
<v-col> <v-col>
<h2>Основные настройки</h2> <h2>Основные настройки</h2>
<br> <br>
<div class=""> <BaseSettings/>
<ul>
<li>
<h3>Ширина схемы:</h3>
<v-text-field v-model="mapWidth"></v-text-field>
</li>
<li>
<h3>Высота схемы:</h3>
<v-text-field v-model="mapHeight"></v-text-field>
</li>
<li>
<h3>Линия:</h3>
<v-dialog max-width="800">
<template v-slot:activator="{ props: activatorProps }">
<v-btn
v-bind="activatorProps"
color="blue"
size="x-large"
variant="flat"
block
text="Конфигурация"
></v-btn>
</template>
<template v-slot:default="{ isActive }">
<v-card title="Конфигурация">
<template v-slot:text>
<h2>Название:</h2>
<v-text-field v-model="store.name"></v-text-field>
<h2>Номер:</h2>
<v-text-field v-model="store.number"></v-text-field>
<h2>Цвет:</h2>
<center>
<v-color-picker v-model="store.color" mode="hex"></v-color-picker>
</center>
</template>
<hr>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text="Закрыть"
variant="text"
@click="isActive.value = false"
></v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</li>
</ul>
</div>
</v-col> </v-col>
<v-col> <v-col>
<h2>Основные настройки</h2> <h2>Станции</h2>
<br> <br>
<div class=""> <NewStation />
<ul> <hr>
<li> <StationsTable />
<h3>Ширина схемы:</h3>
<v-text-field></v-text-field>
</li>
<li>
<h3>Высота схемы:</h3>
<v-text-field></v-text-field>
</li>
<li>
<h3>Ветка:</h3>
</li>
</ul>
</div>
</v-col> </v-col>
<v-col> <v-col>
<h2>Управление</h2> <h2>Управление</h2>
<br> <br>
<div class=""> <div class="">
<v-btn size="x-large" variant="flat" block color="blue">Импорт</v-btn> <v-btn size="x-large" variant="flat" block color="blue" @click="importFile">Импорт</v-btn>
<v-btn size="x-large" variant="flat" block color="orange">Экспорт</v-btn> <v-btn size="x-large" variant="flat" block color="orange">Экспорт</v-btn>
<v-btn size="x-large" variant="flat" block color="green">Сохранить</v-btn> <v-btn size="x-large" variant="flat" block color="green" @click="save">Сохранить</v-btn>
<v-btn size="x-large" variant="flat" block color="red" @click="build">Предпросмотр</v-btn> <v-btn size="x-large" variant="flat" block color="red" @click="build">Предпросмотр</v-btn>
<input type="file" id="fileInput" style="display: none" @change="setFile" accept=".json" />
<hr> <hr>
<br> <br>
<h2>Курсор: ({{ xcord }}; {{ ycord }})</h2> <h2>Курсор: ({{ xcord }}; {{ ycord }})</h2>

View file

@ -0,0 +1,67 @@
<script setup lang="ts">
import {useStore} from '../utils/store.ts';
const store = useStore()
</script>
<template>
<div class="">
<ul>
<li>
<h3>Ширина схемы:</h3>
<v-text-field v-model="store.width"></v-text-field>
</li>
<li>
<h3>Высота схемы:</h3>
<v-text-field v-model="store.height"></v-text-field>
</li>
<li>
<h3>Линия:</h3>
<v-dialog max-width="800">
<template v-slot:activator="{ props: activatorProps }">
<v-btn
v-bind="activatorProps"
color="blue"
size="x-large"
variant="flat"
block
text="Конфигурация"
></v-btn>
</template>
<template v-slot:default="{ isActive }">
<v-card title="Конфигурация">
<template v-slot:text>
<h2>Название:</h2>
<v-text-field v-model="store.name"></v-text-field>
<h2>Номер:</h2>
<v-text-field v-model="store.number"></v-text-field>
<h2>Цвет:</h2>
<center>
<v-color-picker v-model="store.color" mode="hex"></v-color-picker>
</center>
</template>
<hr>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text="Закрыть"
variant="text"
@click="isActive.value = false"
></v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</li>
</ul>
</div>
</template>
<style scoped>
</style>

View file

@ -0,0 +1,113 @@
<script setup lang="ts">
import {useStore} from '../utils/store.ts';
const store = useStore()
</script>
<template>
<v-dialog max-width="800">
<template v-slot:activator="{ props: activatorProps }">
<v-btn
v-bind="activatorProps"
color="purple"
size="x-large"
variant="flat"
block
text="Добавить станцию"
@click="store.stations.push({'name': 'Тест', 'up': true, 'connectedStations': [], 'step': 300})">
</v-btn>
</template>
<template v-slot:default="{ isActive }">
<v-card title="Конфигурация: Новая Станция">
<template v-slot:text>
<h2>Название:</h2>
<v-text-field v-model="store.stations[store.stations.length-1].name"></v-text-field>
<h2>Отображение:</h2>
<v-btn v-if="store.stations[store.stations.length-1].up == true" size="x-large" variant="flat" block color="green" @click="store.stations[store.stations.length-1].up = false">Сверху</v-btn>
<v-btn v-else size="x-large" variant="flat" block color="blue" @click="store.stations[store.stations.length-1].up = true">Снизу</v-btn>
<h2>Шаг (<code>x: 0 {{ store.width - 400 }}</code>):</h2>
<div class="">
<v-slider
class="ma-5"
v-model="store.stations[store.stations.length-1].step"
:max="store.width - 400"
:min="0"
:step="10"
density="compact"
>
<template v-slot:append>
<v-text-field
v-model="store.stations[store.stations.length-1].step"
density="compact"
style="width: 100px"
type="number"
variant="outlined"
hide-details
></v-text-field>
</template>
</v-slider>
</div>
<h2>Связанные станции (переходы):</h2>
<br>
<v-btn
color="purple"
size="x-large"
variant="flat"
block
text="Добавить переход"
@click="store.stations[store.stations.length-1].connectedStations.push({'name': 'Тест', 'branch': {'color': '#fff', 'number': 2}})">
</v-btn>
<v-expansion-panels>
<v-expansion-panel
v-for="i in store.stations[store.stations.length-1].connectedStations.length"
:key="i"
>
<template v-slot:title>
<h2>{{ store.stations[store.stations.length-1].connectedStations[i - 1].name }}</h2>
</template>
<template v-slot:text>
<h3>Название:</h3>
<v-text-field v-model="store.stations[store.stations.length-1].connectedStations[i - 1].name"></v-text-field>
<h3>Номер линии:</h3>
<v-text-field v-model="store.stations[store.stations.length-1].connectedStations[i - 1].branch.number"></v-text-field>
<h2>Цвет:</h2>
<center>
<v-color-picker v-model="store.stations[store.stations.length-1].connectedStations[i - 1].branch.color" mode="hex"></v-color-picker>
</center>
<v-btn
color="red"
size="x-large"
variant="flat"
block
text="Удалить переход"
@click="store.stations[store.stations.length-1].connectedStations.splice(i - 1, 1);">
</v-btn>
</template>
</v-expansion-panel>
</v-expansion-panels>
</template>
<hr>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text="Закрыть"
variant="text"
@click="isActive.value = false"
></v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</template>
<style scoped>
</style>

View file

@ -0,0 +1,143 @@
<script setup lang="ts">
import {useStore} from '../utils/store.ts';
const store = useStore()
</script>
<template>
<v-table style="max-height: 19em">
<thead>
<tr>
<th class="text-left">
Название
</th>
<th class="text-left">
Шаг (<code>x</code>)
</th>
<th class="text-left">Действие</th>
</tr>
</thead>
<tbody>
<tr
v-for="i in store.stations.length"
:key="store.stations[i - 1].name"
>
<td>{{ store.stations[i - 1].name }}</td>
<td>{{ store.stations[i - 1].step }}</td>
<td>
<v-dialog max-width="800">
<template v-slot:activator="{ props: activatorProps }">
<v-btn
icon="mdi-pencil"
variant="text"
v-bind="activatorProps">
</v-btn>
</template>
<template v-slot:default="{ isActive }">
<v-card title="Конфигурация: Станция">
<template v-slot:text>
<h2>Название:</h2>
<v-text-field v-model="store.stations[i-1].name"></v-text-field>
<h2>Отображение:</h2>
<v-btn v-if="store.stations[i-1].up == true" size="x-large" variant="flat" block color="green" @click="store.stations[i-1].up = false">Сверху</v-btn>
<v-btn v-else size="x-large" variant="flat" block color="blue" @click="store.stations[i-1].up = true">Снизу</v-btn>
<h2>Шаг (<code>x: 0 {{ store.width - 400 }}</code>):</h2>
<div class="">
<v-slider
class="ma-5"
v-model="store.stations[i-1].step"
:max="store.width - 400"
:min="0"
:step="10"
density="compact"
>
<template v-slot:append>
<v-text-field
density="compact"
style="width: 100px"
variant="outlined"
hide-details
v-model="store.stations[i-1].step"
></v-text-field>
</template>
</v-slider>
</div>
<h2>Связанные станции (переходы):</h2>
<br>
<v-btn
color="purple"
size="x-large"
variant="flat"
block
text="Добавить переход"
@click="store.stations[i-1].connectedStations.push({'name': 'Тест', 'branch': {'color': '#fff', 'number': 2}})">
</v-btn>
<v-expansion-panels>
<v-expansion-panel
v-for="j in store.stations[i-1].connectedStations.length"
:key="j"
>
<template v-slot:title>
<h2>{{ store.stations[i-1].connectedStations[j - 1].name }}</h2>
</template>
<template v-slot:text>
<h3>Название:</h3>
<v-text-field v-model="store.stations[i-1].connectedStations[j - 1].name"></v-text-field>
<h3>Номер линии:</h3>
<v-text-field v-model="store.stations[i-1].connectedStations[j - 1].branch.number"></v-text-field>
<h2>Цвет:</h2>
<center>
<v-color-picker v-model="store.stations[i-1].connectedStations[j - 1].branch.color" mode="hex"></v-color-picker>
</center>
<v-btn
color="red"
size="x-large"
variant="flat"
block
text="Удалить переход"
@click="store.stations[i-1].connectedStations.splice(j - 1, 1);">
</v-btn>
</template>
</v-expansion-panel>
</v-expansion-panels>
<br>
<hr>
<br>
<v-btn
color="red"
size="x-large"
variant="flat"
block
text="Удалить станцию"
@click="isActive.value = false; store.stations.splice(i - 1, 1);">
</v-btn>
</template>
<hr>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text="Закрыть"
variant="text"
@click="isActive.value = false"
></v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</td>
</tr>
</tbody>
</v-table>
</template>
<style scoped>
</style>

View file

@ -1,3 +1,5 @@
import '@mdi/font/css/materialdesignicons.css'
import { createApp } from 'vue' import { createApp } from 'vue'
import './style.css' import './style.css'
@ -9,13 +11,14 @@ import 'vuetify/styles'
import { createVuetify } from 'vuetify' import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components' import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives' import * as directives from 'vuetify/directives'
import {createPinia} from "pinia"; import {createPinia} from "pinia";
const pinia = createPinia() const pinia = createPinia()
const vuetify = createVuetify({ const vuetify = createVuetify({
components, components,
directives, directives
}) })
createApp(App).use(vuetify).use(pinia).mount('#app') createApp(App).use(vuetify).use(pinia).mount('#app')

View file

@ -11,6 +11,7 @@
.map, svg { .map, svg {
overflow-x: auto; overflow-x: auto;
cursor: crosshair;
} }
.v-btn { .v-btn {
@ -19,4 +20,9 @@
center { center {
padding: 10px; padding: 10px;
}
.v-slider-thumb, .v-slider-thumb__surface, .v-slider-thumb__ripple, .v-expansion-panel, .v-expansion-panel-title,
.v-expansion-panel-title__overlay, .v-expansion-panel__shadow, h2 {
overflow: hidden;
} }

14
src/utils/store.ts Normal file
View file

@ -0,0 +1,14 @@
import {defineStore} from "pinia";
export const useStore = defineStore('branch', {
state: () => {
return {
name: 'Тест',
number: 1,
color: '#0078BE',
stations: [],
width: window.innerWidth,
height: window.innerHeight / 2
}
},
})