feat: 使用全局ip与port配置摄像头
This commit is contained in:
parent
48501d79e2
commit
15f9b68e7d
|
@ -5,19 +5,7 @@
|
||||||
<div class="card bg-base-200 shadow-xl">
|
<div class="card bg-base-200 shadow-xl">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title text-primary">
|
<h2 class="card-title text-primary">
|
||||||
<svg
|
<Settings class="w-6 h-6" />
|
||||||
class="w-6 h-6"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
控制面板
|
控制面板
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
@ -45,19 +33,7 @@
|
||||||
<div class="stats shadow">
|
<div class="stats shadow">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-figure text-secondary">
|
<div class="stat-figure text-secondary">
|
||||||
<svg
|
<Video class="w-8 h-8" />
|
||||||
class="w-8 h-8"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-title">视频规格</div>
|
<div class="stat-title">视频规格</div>
|
||||||
<div class="stat-value text-secondary">
|
<div class="stat-value text-secondary">
|
||||||
|
@ -71,19 +47,7 @@
|
||||||
<div class="stats shadow">
|
<div class="stats shadow">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-figure text-accent">
|
<div class="stat-figure text-accent">
|
||||||
<svg
|
<Users class="w-8 h-8" />
|
||||||
class="w-8 h-8"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-title">连接数</div>
|
<div class="stat-title">连接数</div>
|
||||||
<div class="stat-value text-accent">
|
<div class="stat-value text-accent">
|
||||||
|
@ -124,70 +88,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 摄像头配置 -->
|
|
||||||
<div class="card bg-base-100 shadow-sm mt-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<h3 class="card-title text-sm text-info">
|
|
||||||
<svg
|
|
||||||
class="w-5 h-5"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
摄像头配置
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="flex flex-row justify-around gap-4">
|
|
||||||
<div class="grow">
|
|
||||||
<IpInputField
|
|
||||||
v-model="tempCameraConfig.address"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grow">
|
|
||||||
<PortInputField
|
|
||||||
v-model="tempCameraConfig.port"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-actions justify-end mt-4">
|
|
||||||
<button
|
|
||||||
class="btn btn-ghost"
|
|
||||||
@click="resetCameraConfig"
|
|
||||||
:disabled="isDefaultCamera"
|
|
||||||
>
|
|
||||||
<RotateCcw class="w-4 h-4" />
|
|
||||||
重置
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary"
|
|
||||||
@click="confirmCameraConfig"
|
|
||||||
:disabled="!isValidCameraConfig || !hasChangesCamera"
|
|
||||||
:class="{ loading: configuring }"
|
|
||||||
>
|
|
||||||
<Save class="w-4 h-4" v-if="!configuring" />
|
|
||||||
{{ configuring ? "配置中..." : "保存配置" }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<div class="card-actions justify-end mt-4">
|
<div class="card-actions justify-end mt-4">
|
||||||
<button
|
<button
|
||||||
|
@ -195,26 +95,8 @@
|
||||||
@click="refreshStatus"
|
@click="refreshStatus"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
>
|
>
|
||||||
<svg
|
<RefreshCw v-if="loading" class="animate-spin h-4 w-4 mr-2" />
|
||||||
v-if="loading"
|
<RefreshCw v-else class="h-4 w-4 mr-2" />
|
||||||
class="animate-spin h-4 w-4 mr-2"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<circle
|
|
||||||
class="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="4"
|
|
||||||
></circle>
|
|
||||||
<path
|
|
||||||
class="opacity-75"
|
|
||||||
fill="currentColor"
|
|
||||||
d="m4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
{{ loading ? "刷新中..." : "刷新状态" }}
|
{{ loading ? "刷新中..." : "刷新状态" }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
@ -222,26 +104,8 @@
|
||||||
@click="testConnection"
|
@click="testConnection"
|
||||||
:disabled="testing"
|
:disabled="testing"
|
||||||
>
|
>
|
||||||
<svg
|
<RefreshCw v-if="testing" class="animate-spin h-4 w-4 mr-2" />
|
||||||
v-if="testing"
|
<TestTube v-else class="h-4 w-4 mr-2" />
|
||||||
class="animate-spin h-4 w-4 mr-2"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<circle
|
|
||||||
class="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="4"
|
|
||||||
></circle>
|
|
||||||
<path
|
|
||||||
class="opacity-75"
|
|
||||||
fill="currentColor"
|
|
||||||
d="m4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
{{ testing ? "测试中..." : "测试连接" }}
|
{{ testing ? "测试中..." : "测试连接" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -252,19 +116,7 @@
|
||||||
<div class="card bg-base-200 shadow-xl">
|
<div class="card bg-base-200 shadow-xl">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title text-primary">
|
<h2 class="card-title text-primary">
|
||||||
<svg
|
<Video class="w-6 h-6" />
|
||||||
class="w-6 h-6"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
视频预览
|
视频预览
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
@ -294,20 +146,7 @@
|
||||||
<div class="card bg-error text-white shadow-lg w-full max-w-lg">
|
<div class="card bg-error text-white shadow-lg w-full max-w-lg">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h3 class="card-title flex items-center gap-2">
|
<h3 class="card-title flex items-center gap-2">
|
||||||
<svg
|
<AlertTriangle class="h-6 w-6" />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-6 w-6"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
视频流加载失败
|
视频流加载失败
|
||||||
</h3>
|
</h3>
|
||||||
<p>无法连接到视频服务器,请检查以下内容:</p>
|
<p>无法连接到视频服务器,请检查以下内容:</p>
|
||||||
|
@ -334,19 +173,7 @@
|
||||||
class="absolute inset-0 flex items-center justify-center text-white"
|
class="absolute inset-0 flex items-center justify-center text-white"
|
||||||
>
|
>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<svg
|
<Video class="w-16 h-16 mx-auto mb-4 opacity-50" />
|
||||||
class="w-16 h-16 mx-auto mb-4 opacity-50"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<p class="text-lg opacity-75">{{ videoStatus }}</p>
|
<p class="text-lg opacity-75">{{ videoStatus }}</p>
|
||||||
<p class="text-sm opacity-60 mt-2">
|
<p class="text-sm opacity-60 mt-2">
|
||||||
点击"播放视频流"按钮开始查看实时视频
|
点击"播放视频流"按钮开始查看实时视频
|
||||||
|
@ -370,20 +197,7 @@
|
||||||
role="button"
|
role="button"
|
||||||
class="btn btn-sm btn-outline btn-accent"
|
class="btn btn-sm btn-outline btn-accent"
|
||||||
>
|
>
|
||||||
<svg
|
<MoreHorizontal class="w-4 h-4 mr-1" />
|
||||||
class="w-4 h-4 mr-1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
更多功能
|
更多功能
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
|
@ -391,15 +205,22 @@
|
||||||
class="dropdown-content z-[1] menu p-2 shadow bg-base-200 rounded-box w-52"
|
class="dropdown-content z-[1] menu p-2 shadow bg-base-200 rounded-box w-52"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<a @click="openInNewTab(streamInfo.htmlUrl)"
|
<a @click="openInNewTab(streamInfo.htmlUrl)">
|
||||||
>在新标签打开视频页面</a
|
<ExternalLink class="w-4 h-4" />
|
||||||
>
|
在新标签打开视频页面
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a @click="takeSnapshot">获取并下载快照</a></li>
|
|
||||||
<li>
|
<li>
|
||||||
<a @click="copyToClipboard(streamInfo.mjpegUrl)"
|
<a @click="takeSnapshot">
|
||||||
>复制MJPEG地址</a
|
<Camera class="w-4 h-4" />
|
||||||
>
|
获取并下载快照
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a @click="copyToClipboard(streamInfo.mjpegUrl)">
|
||||||
|
<Copy class="w-4 h-4" />
|
||||||
|
复制MJPEG地址
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -408,25 +229,7 @@
|
||||||
@click="startStream"
|
@click="startStream"
|
||||||
:disabled="isPlaying"
|
:disabled="isPlaying"
|
||||||
>
|
>
|
||||||
<svg
|
<Play class="w-4 h-4 mr-1" />
|
||||||
class="w-4 h-4 mr-1"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
播放视频流
|
播放视频流
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
@ -434,25 +237,7 @@
|
||||||
@click="stopStream"
|
@click="stopStream"
|
||||||
:disabled="!isPlaying"
|
:disabled="!isPlaying"
|
||||||
>
|
>
|
||||||
<svg
|
<Square class="w-4 h-4 mr-1" />
|
||||||
class="w-4 h-4 mr-1"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
停止视频流
|
停止视频流
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -464,19 +249,7 @@
|
||||||
<div class="card bg-base-200 shadow-xl">
|
<div class="card bg-base-200 shadow-xl">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title text-primary">
|
<h2 class="card-title text-primary">
|
||||||
<svg
|
<FileText class="w-6 h-6" />
|
||||||
class="w-6 h-6"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
操作日志
|
操作日志
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
@ -511,21 +284,30 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, reactive, watch, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
import { useStorage } from "@vueuse/core";
|
|
||||||
import { z } from "zod";
|
|
||||||
import {
|
import {
|
||||||
Save,
|
Settings,
|
||||||
RotateCcw,
|
Video,
|
||||||
|
Users,
|
||||||
|
RefreshCw,
|
||||||
|
TestTube,
|
||||||
|
Play,
|
||||||
|
Square,
|
||||||
|
ExternalLink,
|
||||||
|
Camera,
|
||||||
|
Copy,
|
||||||
|
FileText,
|
||||||
|
AlertTriangle,
|
||||||
|
MoreHorizontal,
|
||||||
} from "lucide-vue-next";
|
} from "lucide-vue-next";
|
||||||
import { VideoStreamClient, CameraConfigRequest } from "@/APIClient";
|
import { VideoStreamClient, CameraConfigRequest } from "@/APIClient";
|
||||||
import { IpInputField, PortInputField } from "@/components/InputField";
|
import { useEquipments } from "@/stores/equipments";
|
||||||
|
|
||||||
|
const eqps = useEquipments();
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const testing = ref(false);
|
const testing = ref(false);
|
||||||
const configuring = ref(false);
|
|
||||||
const testingCamera = ref(false);
|
|
||||||
const isPlaying = ref(false);
|
const isPlaying = ref(false);
|
||||||
const hasVideoError = ref(false);
|
const hasVideoError = ref(false);
|
||||||
const videoStatus = ref('点击"播放视频流"按钮开始查看实时视频');
|
const videoStatus = ref('点击"播放视频流"按钮开始查看实时视频');
|
||||||
|
@ -551,92 +333,6 @@ const streamInfo = ref({
|
||||||
snapshotUrl: "",
|
snapshotUrl: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// 摄像头配置类型定义
|
|
||||||
const cameraConfigSchema = z.object({
|
|
||||||
address: z
|
|
||||||
.string()
|
|
||||||
.ip({ version: "v4", message: "请输入有效的IPv4地址" })
|
|
||||||
.min(1, "请输入IP地址"),
|
|
||||||
port: z
|
|
||||||
.number()
|
|
||||||
.int("端口必须是整数")
|
|
||||||
.min(1, "端口必须大于0")
|
|
||||||
.max(65535, "端口必须小于等于65535"),
|
|
||||||
});
|
|
||||||
|
|
||||||
type CameraConfig = z.infer<typeof cameraConfigSchema>;
|
|
||||||
|
|
||||||
// 默认摄像头配置
|
|
||||||
const defaultCameraConfig: CameraConfig = {
|
|
||||||
address: "192.168.1.100",
|
|
||||||
port: 8080,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 使用 VueUse 存储摄像头配置
|
|
||||||
const cameraConfig = useStorage<CameraConfig>(
|
|
||||||
"camera-config",
|
|
||||||
defaultCameraConfig,
|
|
||||||
localStorage,
|
|
||||||
{
|
|
||||||
serializer: {
|
|
||||||
read: (value: string) => {
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(value);
|
|
||||||
const result = cameraConfigSchema.safeParse(parsed);
|
|
||||||
return result.success ? result.data : defaultCameraConfig;
|
|
||||||
} catch {
|
|
||||||
return defaultCameraConfig;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
write: (value: CameraConfig) => JSON.stringify(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// 临时摄像头配置(用于编辑)
|
|
||||||
const tempCameraConfig = reactive<CameraConfig>({
|
|
||||||
address: cameraConfig.value.address,
|
|
||||||
port: cameraConfig.value.port,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 摄像头配置验证
|
|
||||||
const isValidCameraConfig = computed(() => {
|
|
||||||
return tempCameraConfig.address && tempCameraConfig.port &&
|
|
||||||
tempCameraConfig.port >= 1 && tempCameraConfig.port <= 65535 &&
|
|
||||||
/^(\d{1,3}\.){3}\d{1,3}$/.test(tempCameraConfig.address);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 检查摄像头配置是否有更改
|
|
||||||
const hasChangesCamera = computed(() => {
|
|
||||||
return (
|
|
||||||
tempCameraConfig.address !== cameraConfig.value.address ||
|
|
||||||
tempCameraConfig.port !== cameraConfig.value.port
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const isDefaultCamera = computed(() => {
|
|
||||||
return (
|
|
||||||
defaultCameraConfig.address === tempCameraConfig.address &&
|
|
||||||
defaultCameraConfig.port === tempCameraConfig.port
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 重置摄像头配置
|
|
||||||
const resetCameraConfig = () => {
|
|
||||||
tempCameraConfig.address = defaultCameraConfig.address;
|
|
||||||
tempCameraConfig.port = defaultCameraConfig.port;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听存储的摄像头配置变化,同步到临时配置
|
|
||||||
watch(
|
|
||||||
cameraConfig,
|
|
||||||
(newConfig) => {
|
|
||||||
tempCameraConfig.address = newConfig.address;
|
|
||||||
tempCameraConfig.port = newConfig.port;
|
|
||||||
},
|
|
||||||
{ deep: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentVideoSource = ref("");
|
const currentVideoSource = ref("");
|
||||||
const logs = ref<Array<{ time: Date; level: string; message: string }>>([]);
|
const logs = ref<Array<{ time: Date; level: string; message: string }>>([]);
|
||||||
|
|
||||||
|
@ -656,71 +352,6 @@ const addLog = (level: string, message: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载摄像头配置
|
|
||||||
const loadCameraConfig = async () => {
|
|
||||||
try {
|
|
||||||
addLog("info", "正在加载摄像头配置...");
|
|
||||||
|
|
||||||
const config = await videoClient.getCameraConfig();
|
|
||||||
if (config && config.address && config.port) {
|
|
||||||
// 更新存储的配置
|
|
||||||
cameraConfig.value = {
|
|
||||||
address: config.address,
|
|
||||||
port: config.port,
|
|
||||||
};
|
|
||||||
addLog("success", `摄像头配置加载成功: ${config.address}:${config.port}`);
|
|
||||||
} else {
|
|
||||||
addLog("warning", "未找到保存的摄像头配置,使用默认值");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
addLog("error", `加载摄像头配置失败: ${error}`);
|
|
||||||
console.error("加载摄像头配置失败:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 确认摄像头配置
|
|
||||||
const confirmCameraConfig = async () => {
|
|
||||||
if (!isValidCameraConfig.value) return;
|
|
||||||
|
|
||||||
configuring.value = true;
|
|
||||||
try {
|
|
||||||
addLog(
|
|
||||||
"info",
|
|
||||||
`正在配置摄像头: ${tempCameraConfig.address}:${tempCameraConfig.port}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 模拟保存延迟
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
||||||
|
|
||||||
// 保存配置
|
|
||||||
cameraConfig.value = {
|
|
||||||
address: tempCameraConfig.address,
|
|
||||||
port: tempCameraConfig.port,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 使用新配置调用API
|
|
||||||
const result = await videoClient.configureCamera(
|
|
||||||
CameraConfigRequest.fromJS({
|
|
||||||
address: tempCameraConfig.address,
|
|
||||||
port: tempCameraConfig.port,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
addLog("success", "摄像头配置保存成功");
|
|
||||||
// 配置成功后刷新状态
|
|
||||||
await refreshStatus();
|
|
||||||
} else {
|
|
||||||
addLog("error", "摄像头配置保存失败");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
addLog("error", `配置摄像头失败: ${error}`);
|
|
||||||
console.error("配置摄像头失败:", error);
|
|
||||||
} finally {
|
|
||||||
configuring.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 格式化时间
|
// 格式化时间
|
||||||
const formatTime = (time: Date) => {
|
const formatTime = (time: Date) => {
|
||||||
return time.toLocaleTimeString();
|
return time.toLocaleTimeString();
|
||||||
|
@ -798,6 +429,13 @@ const takeSnapshot = async () => {
|
||||||
const refreshStatus = async () => {
|
const refreshStatus = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
|
addLog("info", "正在配置并初始化摄像头...");
|
||||||
|
const boardconfig = new CameraConfigRequest({
|
||||||
|
address: eqps.boardAddr,
|
||||||
|
port: eqps.boardPort,
|
||||||
|
});
|
||||||
|
await videoClient.configureCamera(boardconfig);
|
||||||
|
|
||||||
addLog("info", "正在获取服务状态...");
|
addLog("info", "正在获取服务状态...");
|
||||||
|
|
||||||
// 使用新的API方法名称
|
// 使用新的API方法名称
|
||||||
|
@ -909,7 +547,6 @@ const stopStream = () => {
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
addLog("info", "HTTP 视频流页面已加载");
|
addLog("info", "HTTP 视频流页面已加载");
|
||||||
await loadCameraConfig();
|
|
||||||
await refreshStatus();
|
await refreshStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue