first commit
This commit is contained in:
11
target/classes/application.yml
Normal file
11
target/classes/application.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
jt808:
|
||||
port: 20048
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
com.hua.transport.jt808: DEBUG
|
||||
|
||||
187
target/classes/static/index.html
Normal file
187
target/classes/static/index.html
Normal file
@@ -0,0 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>JT808 Server Dashboard</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f8f9fa; }
|
||||
.dashboard-card { transition: all 0.3s; }
|
||||
.dashboard-card:hover { transform: translateY(-5px); shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
||||
.log-console {
|
||||
background-color: #1e1e1e;
|
||||
color: #00ff00;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.log-entry { margin-bottom: 5px; border-bottom: 1px solid #333; padding-bottom: 2px; }
|
||||
.log-time { color: #888; margin-right: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<nav class="navbar navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<span class="navbar-brand mb-0 h1">JT808 Transport Server</span>
|
||||
<span class="text-light">
|
||||
Status:
|
||||
<span class="badge" :class="connected ? 'bg-success' : 'bg-danger'">
|
||||
{{ connected ? 'Connected' : 'Disconnected' }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<!-- Device Simulation Card -->
|
||||
<div class="col-md-5">
|
||||
<div class="card dashboard-card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
Device Simulation (Typed API)
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form @submit.prevent="sendReport">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">IMEI</label>
|
||||
<input v-model="form.imei" class="form-control" placeholder="123456789012">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label">Lat</label>
|
||||
<input v-model.number="form.lat" type="number" step="0.000001" class="form-control">
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label">Lon</label>
|
||||
<input v-model.number="form.lon" type="number" step="0.000001" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Send Location</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generic Upload Test -->
|
||||
<div class="card dashboard-card mb-4">
|
||||
<div class="card-header bg-info text-white">
|
||||
Universal Upload Test (/upload)
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Arbitrary JSON Payload</label>
|
||||
<textarea v-model="customJson" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
<button @click="sendCustomJson" class="btn btn-info text-white w-100">Send to /upload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Live Log Console -->
|
||||
<div class="col-md-7">
|
||||
<div class="card dashboard-card h-100">
|
||||
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center">
|
||||
<span>Live Data Stream (/api/v1/device/upload)</span>
|
||||
<button @click="logs = []" class="btn btn-sm btn-outline-secondary">Clear</button>
|
||||
</div>
|
||||
<div class="card-body bg-dark p-0">
|
||||
<div class="log-console" ref="console">
|
||||
<div v-if="logs.length === 0" class="text-muted text-center mt-5">Waiting for data...</div>
|
||||
<div v-for="(log, index) in logs" :key="index" class="log-entry">
|
||||
<span class="log-time">[{{ log.time }}]</span>
|
||||
<span>{{ log.data }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp } = Vue
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
imei: '13800138000',
|
||||
lat: 34.215432,
|
||||
lon: 108.924231,
|
||||
speed: 60.5
|
||||
},
|
||||
customJson: '{\n "sensor": "temp-01",\n "value": 25.5,\n "unit": "C"\n}',
|
||||
logs: [],
|
||||
connected: false,
|
||||
eventSource: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.connectSSE();
|
||||
},
|
||||
methods: {
|
||||
connectSSE() {
|
||||
this.eventSource = new EventSource('/api/v1/device/logs/stream');
|
||||
|
||||
this.eventSource.onopen = () => {
|
||||
this.connected = true;
|
||||
this.addLog('System connected. Listening for /upload events...');
|
||||
};
|
||||
|
||||
this.eventSource.onerror = () => {
|
||||
this.connected = false;
|
||||
this.eventSource.close();
|
||||
// Reconnect after 3s
|
||||
setTimeout(() => this.connectSSE(), 3000);
|
||||
};
|
||||
|
||||
this.eventSource.addEventListener('api-log', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
this.addLog(data);
|
||||
});
|
||||
},
|
||||
addLog(data) {
|
||||
const now = new Date().toLocaleTimeString();
|
||||
this.logs.unshift({
|
||||
time: now,
|
||||
data: typeof data === 'string' ? data : JSON.stringify(data)
|
||||
});
|
||||
// Keep last 50 logs
|
||||
if (this.logs.length > 50) this.logs.pop();
|
||||
},
|
||||
async sendReport() {
|
||||
try {
|
||||
await fetch('/api/v1/device/location', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(this.form)
|
||||
});
|
||||
// Note: /location endpoint doesn't broadcast to SSE in this demo, only /upload does
|
||||
// But we could add it if needed.
|
||||
} catch (e) {
|
||||
alert('Error: ' + e.message);
|
||||
}
|
||||
},
|
||||
async sendCustomJson() {
|
||||
try {
|
||||
const res = await fetch('/api/v1/device/upload', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: this.customJson
|
||||
});
|
||||
const result = await res.json();
|
||||
if (result.code !== 200) alert('Error: ' + result.message);
|
||||
} catch (e) {
|
||||
alert('Error: ' + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).mount('#app')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user