feat: 更新api,并更新了串流页面
This commit is contained in:
@@ -216,11 +216,16 @@ export class VideoStreamClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试摄像头连接
|
||||
* @return 连接测试结果
|
||||
* 控制 HTTP 视频流服务开关
|
||||
* @param enabled (optional) 是否启用服务
|
||||
* @return 操作结果
|
||||
*/
|
||||
testCameraConnection(): Promise<any> {
|
||||
let url_ = this.baseUrl + "/api/VideoStream/TestCameraConnection";
|
||||
setEnabled(enabled: boolean | undefined): Promise<any> {
|
||||
let url_ = this.baseUrl + "/api/VideoStream/SetEnabled?";
|
||||
if (enabled === null)
|
||||
throw new Error("The parameter 'enabled' cannot be null.");
|
||||
else if (enabled !== undefined)
|
||||
url_ += "enabled=" + encodeURIComponent("" + enabled) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
@@ -231,11 +236,11 @@ export class VideoStreamClient {
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processTestCameraConnection(_response);
|
||||
return this.processSetEnabled(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processTestCameraConnection(response: Response): Promise<any> {
|
||||
protected processSetEnabled(response: Response): Promise<any> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
@@ -2541,4 +2546,4 @@ function throwException(message: string, status: number, response: string, heade
|
||||
throw result;
|
||||
else
|
||||
throw new ApiException(message, status, response, headers, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,69 +149,40 @@
|
||||
</svg>
|
||||
摄像头配置
|
||||
</h3>
|
||||
<div class="flex flex-row justify-between items-center gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">IP地址</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="cameraConfig.address"
|
||||
placeholder="例如: 192.168.1.100"
|
||||
class="input input-bordered input-sm"
|
||||
|
||||
<div class="flex flex-row justify-around gap-4">
|
||||
<div class="grow">
|
||||
<IpInputField
|
||||
v-model="tempCameraConfig.address"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">端口号</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
v-model="cameraConfig.port"
|
||||
placeholder="例如: 8080"
|
||||
class="input input-bordered input-sm"
|
||||
|
||||
<div class="grow">
|
||||
<PortInputField
|
||||
v-model="tempCameraConfig.port"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-actions justify-end mt-4">
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="confirmCameraConfig"
|
||||
:disabled="configuring"
|
||||
class="btn btn-ghost"
|
||||
@click="resetCameraConfig"
|
||||
:disabled="isDefaultCamera"
|
||||
>
|
||||
<svg
|
||||
v-if="configuring"
|
||||
class="animate-spin h-4 w-4 mr-1"
|
||||
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>
|
||||
<svg
|
||||
v-else
|
||||
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="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
{{ configuring ? "配置中..." : "确认" }}
|
||||
<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>
|
||||
@@ -540,8 +511,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { ref, computed, reactive, watch, onMounted, onUnmounted } from "vue";
|
||||
import { useStorage } from "@vueuse/core";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
Save,
|
||||
RotateCcw,
|
||||
} from "lucide-vue-next";
|
||||
import { VideoStreamClient, CameraConfigRequest } from "@/APIClient";
|
||||
import { IpInputField, PortInputField } from "@/components/InputField";
|
||||
|
||||
// 状态管理
|
||||
const loading = ref(false);
|
||||
@@ -573,12 +551,92 @@ const streamInfo = ref({
|
||||
snapshotUrl: "",
|
||||
});
|
||||
|
||||
// 摄像头配置
|
||||
const cameraConfig = ref({
|
||||
// 摄像头配置类型定义
|
||||
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 logs = ref<Array<{ time: Date; level: string; message: string }>>([]);
|
||||
|
||||
@@ -605,8 +663,11 @@ const loadCameraConfig = async () => {
|
||||
|
||||
const config = await videoClient.getCameraConfig();
|
||||
if (config && config.address && config.port) {
|
||||
cameraConfig.value.address = config.address;
|
||||
cameraConfig.value.port = config.port;
|
||||
// 更新存储的配置
|
||||
cameraConfig.value = {
|
||||
address: config.address,
|
||||
port: config.port,
|
||||
};
|
||||
addLog("success", `摄像头配置加载成功: ${config.address}:${config.port}`);
|
||||
} else {
|
||||
addLog("warning", "未找到保存的摄像头配置,使用默认值");
|
||||
@@ -619,20 +680,30 @@ const loadCameraConfig = async () => {
|
||||
|
||||
// 确认摄像头配置
|
||||
const confirmCameraConfig = async () => {
|
||||
if (!cameraConfig.value.address || !cameraConfig.value.port) {
|
||||
addLog("error", "请填写完整的摄像头IP地址和端口号");
|
||||
return;
|
||||
}
|
||||
if (!isValidCameraConfig.value) return;
|
||||
|
||||
configuring.value = true;
|
||||
try {
|
||||
addLog(
|
||||
"info",
|
||||
`正在配置摄像头: ${cameraConfig.value.address}:${cameraConfig.value.port}`,
|
||||
`正在配置摄像头: ${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(cameraConfig.value),
|
||||
CameraConfigRequest.fromJS({
|
||||
address: tempCameraConfig.address,
|
||||
port: tempCameraConfig.port,
|
||||
}),
|
||||
);
|
||||
|
||||
if (result) {
|
||||
@@ -650,35 +721,6 @@ const confirmCameraConfig = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 测试摄像头连接
|
||||
const testCameraConnection = async () => {
|
||||
if (!cameraConfig.value.address || !cameraConfig.value.port) {
|
||||
addLog("error", "请先配置摄像头IP地址和端口号");
|
||||
return;
|
||||
}
|
||||
|
||||
testingCamera.value = true;
|
||||
try {
|
||||
addLog(
|
||||
"info",
|
||||
`正在测试摄像头连接: ${cameraConfig.value.address}:${cameraConfig.value.port}`,
|
||||
);
|
||||
|
||||
const result = await videoClient.testCameraConnection();
|
||||
|
||||
if (result && result.success) {
|
||||
addLog("success", "摄像头连接测试成功");
|
||||
} else {
|
||||
addLog("error", `摄像头连接测试失败: ${result?.message || "未知错误"}`);
|
||||
}
|
||||
} catch (error) {
|
||||
addLog("error", `摄像头连接测试失败: ${error}`);
|
||||
console.error("摄像头连接测试失败:", error);
|
||||
} finally {
|
||||
testingCamera.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: Date) => {
|
||||
return time.toLocaleTimeString();
|
||||
@@ -823,6 +865,7 @@ const startStream = async () => {
|
||||
try {
|
||||
addLog("info", "正在启动视频流...");
|
||||
videoStatus.value = "正在连接视频流...";
|
||||
videoClient.setEnabled(true);
|
||||
|
||||
// 刷新状态
|
||||
await refreshStatus();
|
||||
@@ -846,6 +889,7 @@ const startStream = async () => {
|
||||
const stopStream = () => {
|
||||
try {
|
||||
addLog("info", "正在停止视频流...");
|
||||
videoClient.setEnabled(false);
|
||||
|
||||
// 清除视频源
|
||||
currentVideoSource.value = "";
|
||||
|
||||
Reference in New Issue
Block a user