add update route

This commit is contained in:
Владимир Фёдоров 2025-12-07 03:40:29 +07:00
parent 7225ce7f29
commit a62683d9ac
3 changed files with 116 additions and 79 deletions

View File

@ -2,35 +2,12 @@
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import HeaderBlock from './HeaderBlock.vue'; import HeaderBlock from './HeaderBlock.vue';
import { Network } from 'vis-network' import { Network, type Data } from 'vis-network'
import { getGraph } from './client'; import { getGraph, updateNode } from './client';
import type { Graph, GraphEdge, GraphNode } from './models';
const network = ref<HTMLElement>() const network = ref<HTMLElement>()
type GraphApplication = {
name: string,
}
type GraphNode = {
id: number,
label: string,
name: string,
text: string,
applications: Array<GraphApplication>,
links: Array<GraphNode>,
};
type GraphEdge = {
from: number,
to: number,
arrows: string,
};
type Graph = {
nodes: Array<GraphNode>;
edges: Array<GraphEdge>;
};
const graph = ref<Graph>({ const graph = ref<Graph>({
nodes: [], nodes: [],
edges: [] edges: []
@ -46,16 +23,19 @@ const selectedNode = ref<GraphNode>({
}) })
let net = <Network>{} let net = <Network>{}
let data = <Data>{}
onMounted(async () => { async function loadGraph() {
if (!network.value) return
graph.value = await getGraph() graph.value = await getGraph()
data = {
const data = {
nodes: graph.value.nodes, nodes: graph.value.nodes,
edges: graph.value.edges edges: graph.value.edges
} }
net.setData(data)
}
onMounted(async () => {
if (!network.value) return
const options = { const options = {
interaction: { interaction: {
@ -85,6 +65,7 @@ onMounted(async () => {
} }
}); });
await loadGraph()
selectNode(graph.value.nodes[0]) selectNode(graph.value.nodes[0])
}) })
@ -103,6 +84,12 @@ function selectNode(node: GraphNode) {
net.selectNodes([selectedNode.value.id]) net.selectNodes([selectedNode.value.id])
} }
async function updateSelectedNode() {
console.log("Update node:", selectedNode.value)
await updateNode(selectedNode.value)
await loadGraph()
}
function nodeHeader(node: GraphNode): string { function nodeHeader(node: GraphNode): string {
return "[" + node.label + "] - " + node.name return "[" + node.label + "] - " + node.name
} }
@ -119,6 +106,9 @@ function nodeHeader(node: GraphNode): string {
<div class="nodes-container"> <div class="nodes-container">
<h2>Точки</h2> <h2>Точки</h2>
<div>Всего точек: {{ graph.nodes.length }}</div>
<div>Всего связей: {{ graph.edges.length }}</div>
<hr class="hr">
<div v-bind:key="node.id" v-for="node in graph.nodes"> <div v-bind:key="node.id" v-for="node in graph.nodes">
<div :class="[node.id == selectedNode.id ? 'selected-node' : '']" class="node-select-button" <div :class="[node.id == selectedNode.id ? 'selected-node' : '']" class="node-select-button"
@ -134,22 +124,25 @@ function nodeHeader(node: GraphNode): string {
{{ nodeHeader(selectedNode) }} {{ nodeHeader(selectedNode) }}
</div> </div>
<div> <div>
{{ selectedNode.text }} <textarea class="node-text-edit-field" rows="30" v-model="selectedNode.text"></textarea>
</div> </div>
<div> <div>
<h3>Приложения</h3> <h3>Приложения: {{ selectedNode.applications.length }}</h3>
<div v-bind:key="application.name" v-for="application in selectedNode.applications"> <div v-bind:key="application.name" v-for="application in selectedNode.applications">
{{ application.name }} - {{ application.name }}
</div> </div>
</div> </div>
<div> <div>
<h3>Ссылки</h3> <h3>Ссылки: {{ selectedNode.links.length }}</h3>
<div v-bind:key="node.id" v-for="node in selectedNode.links"> <div v-bind:key="node.id" v-for="node in selectedNode.links">
<div class="node-select-button" v-on:click="selectNode(node)"> <div class="node-select-button" v-on:click="selectNode(node)">
{{ nodeHeader(node) }} - {{ nodeHeader(node) }}
</div> </div>
</div> </div>
</div> </div>
<div>
<button class="node-edit-save-button" v-on:click="updateSelectedNode()">Сохранить</button>
</div>
</div> </div>
</template> </template>
@ -175,7 +168,8 @@ function nodeHeader(node: GraphNode): string {
top: 55px; top: 55px;
height: calc(100vh - 100px); height: calc(100vh - 100px);
padding: 10px 20px; padding: 10px 20px;
max-width: 300px; min-width: 350px;
max-width: 400px;
} }
.node-select-button { .node-select-button {
@ -192,4 +186,27 @@ function nodeHeader(node: GraphNode): string {
color: #960000; color: #960000;
cursor: pointer; cursor: pointer;
} }
.node-text-edit-field {
padding: 7px;
margin: 5px 0;
width: 100%;
}
.node-edit-save-button {
padding: 3px 7px;
margin: 5px;
background-color: #ffffff;
border-radius: 7px;
border: 1px solid #373737;
}
.node-edit-save-button:hover {
background-color: #dddddd;
cursor: pointer;
}
.hr {
margin: 10px 0;
}
</style> </style>

View File

@ -1,11 +1,9 @@
import type { Game, Teams } from './models'; import type { Game, GraphNode, Teams } from './models'
import { downloadData } from './qr'; import { downloadData } from './qr'
export const apiGetTeams = async (): Promise<Teams> => { export const apiGetTeams = async (): Promise<Teams> => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/teams'))
getApiUrl("/teams")
)
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
@ -18,15 +16,12 @@ export const apiGetTeams = async (): Promise<Teams> => {
export const apiAddTeam = async (teamName: string) => { export const apiAddTeam = async (teamName: string) => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/teams'), {
getApiUrl("/teams"), method: 'POST',
{ body: JSON.stringify({
method: "POST", teams: [{ name: teamName }],
body: JSON.stringify({ }),
"teams": [{ "name": teamName }] })
})
}
)
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
@ -38,9 +33,7 @@ export const apiAddTeam = async (teamName: string) => {
export const apiGetGame = async (): Promise<Game> => { export const apiGetGame = async (): Promise<Game> => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/game'))
getApiUrl("/game")
)
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
@ -53,10 +46,7 @@ export const apiGetGame = async (): Promise<Game> => {
export const apiStartGame = async () => { export const apiStartGame = async () => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/game/start'), { method: 'POST' })
getApiUrl("/game/start"),
{ method: "POST" }
)
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
@ -68,10 +58,7 @@ export const apiStartGame = async () => {
export const apiStopGame = async () => { export const apiStopGame = async () => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/game/stop'), { method: 'POST' })
getApiUrl("/game/stop"),
{ method: "POST" }
)
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
@ -83,15 +70,12 @@ export const apiStopGame = async () => {
export const apiGaveApplication = async (teamId: number, id: number) => { export const apiGaveApplication = async (teamId: number, id: number) => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/teams/' + teamId + '/applications'), {
getApiUrl("/teams/" + teamId + "/applications"), method: 'POST',
{ body: JSON.stringify({
method: "POST", applications: [{ id: id }],
body: JSON.stringify({ }),
"applications": [{ "id": id }] })
})
}
)
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
@ -103,13 +87,11 @@ export const apiGaveApplication = async (teamId: number, id: number) => {
export const apiDownloadQrCodesFile = async () => { export const apiDownloadQrCodesFile = async () => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/teams/pdf'))
getApiUrl("/teams/pdf")
)
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
const data = await response.json(); const data = await response.json()
downloadData(data.result) downloadData(data.result)
} catch (error) { } catch (error) {
console.error('[apiDownloadQrCodesFile] error:', error) console.error('[apiDownloadQrCodesFile] error:', error)
@ -119,9 +101,23 @@ export const apiDownloadQrCodesFile = async () => {
export const getGraph = async () => { export const getGraph = async () => {
try { try {
const response = await fetch( const response = await fetch(getApiUrl('/graph'))
getApiUrl("/graph") if (!response.ok) {
) throw new Error(`http error status: ${response.status}`)
}
return await response.json()
} catch (error) {
console.error('[apiDownloadQrCodesFile] error:', error)
throw error
}
}
export const updateNode = async (node: GraphNode) => {
try {
const response = await fetch(getApiUrl('/graph/nodes'), {
method: 'PUT',
body: JSON.stringify({ node: node }),
})
if (!response.ok) { if (!response.ok) {
throw new Error(`http error status: ${response.status}`) throw new Error(`http error status: ${response.status}`)
} }
@ -133,5 +129,5 @@ export const getGraph = async () => {
} }
function getApiUrl(path: string) { function getApiUrl(path: string) {
return "http://" + window.location.host.split(":")[0] + ":8090" + path return 'http://' + window.location.host.split(':')[0] + ':8090' + path
} }

View File

@ -21,3 +21,27 @@ export type Game = {
startAt: string startAt: string
endAt: string endAt: string
} }
export type Graph = {
nodes: Array<GraphNode>
edges: Array<GraphEdge>
}
export type GraphNode = {
id: number
label: string
name: string
text: string
applications: Array<GraphApplication>
links: Array<GraphNode>
}
export type GraphEdge = {
from: number
to: number
arrows: string
}
export type GraphApplication = {
name: string
}