唐润平:解决了编辑模式bug
This commit is contained in:
@@ -64,10 +64,10 @@ import {
|
|||||||
|
|
||||||
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
|
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
|
||||||
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
|
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
|
||||||
import { onMounted, reactive, ref, watch } from "vue";
|
import { onMounted, reactive, ref, toRaw, watch } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { deleteEquipment, initData } from "./request";
|
import { deleteEquipment, initData } from "./request";
|
||||||
|
import { useModelSceneStore } from "@/store/modelSceneStore";
|
||||||
// 获取html标签跟随组件dom
|
// 获取html标签跟随组件dom
|
||||||
const content = ref(null);
|
const content = ref(null);
|
||||||
const info = ref(null);
|
const info = ref(null);
|
||||||
@@ -77,7 +77,7 @@ let demo; //定义demo对象
|
|||||||
const loader = new OBJLoader();
|
const loader = new OBJLoader();
|
||||||
let hdrLoader = new RGBELoader();
|
let hdrLoader = new RGBELoader();
|
||||||
let backColorSet = three.sRGBEncoding;
|
let backColorSet = three.sRGBEncoding;
|
||||||
|
const modelStore = useModelSceneStore();
|
||||||
const params = defineProps(["isedit", "tunnelId", "tunnelLength", "form"]); //接收参数看是不是编辑模式,如果是编辑模式,则需要做一些处理
|
const params = defineProps(["isedit", "tunnelId", "tunnelLength", "form"]); //接收参数看是不是编辑模式,如果是编辑模式,则需要做一些处理
|
||||||
|
|
||||||
let isedit = ref(params.isedit);
|
let isedit = ref(params.isedit);
|
||||||
@@ -123,8 +123,13 @@ async function handleMounted() {
|
|||||||
map.set("equ_fan", await loadModel("/devicesModel/model2.obj"));
|
map.set("equ_fan", await loadModel("/devicesModel/model2.obj"));
|
||||||
map.set("equ_sensors", await loadModel("/devicesModel/sensors.obj"));
|
map.set("equ_sensors", await loadModel("/devicesModel/sensors.obj"));
|
||||||
demo.initDevicesModel(map);
|
demo.initDevicesModel(map);
|
||||||
// 初始化
|
// 初始化渲染设备
|
||||||
modelList.value = await initData(params.tunnelId, params.form);
|
modelList.value = await modelStore.initModelData(
|
||||||
|
params.tunnelId,
|
||||||
|
params.form
|
||||||
|
);
|
||||||
|
// modelList.value = await initData(params.tunnelId, params.form);
|
||||||
|
console.log("test", modelList.value);
|
||||||
demo.editTunnelInit(modelList.value);
|
demo.editTunnelInit(modelList.value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
@@ -228,7 +233,8 @@ function handleRemoveEqu() {
|
|||||||
// 对话框确认删除
|
// 对话框确认删除
|
||||||
function handleConfirmAddEqu() {
|
function handleConfirmAddEqu() {
|
||||||
console.log("target:", targetP.value.info);
|
console.log("target:", targetP.value.info);
|
||||||
deleteEquipment(targetP.value.info.equipmentId, pointGap)
|
modelStore
|
||||||
|
.deleteEquipment(targetP.value.info.equipmentId, pointGap)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
demo.removeEquipment(targetP.value);
|
demo.removeEquipment(targetP.value);
|
||||||
centerDialogVisible.value = false;
|
centerDialogVisible.value = false;
|
||||||
|
|||||||
@@ -47,9 +47,18 @@
|
|||||||
import InputNum from "./childComps/InputNum.vue";
|
import InputNum from "./childComps/InputNum.vue";
|
||||||
import SelectInput from "./childComps/SelectInput.vue";
|
import SelectInput from "./childComps/SelectInput.vue";
|
||||||
import { getEquipmentType, getEquipment } from "@/api/tunnelScene";
|
import { getEquipmentType, getEquipment } from "@/api/tunnelScene";
|
||||||
import { reactive, computed, defineEmits, defineProps, watch, ref } from "vue";
|
import {
|
||||||
|
reactive,
|
||||||
|
computed,
|
||||||
|
defineEmits,
|
||||||
|
defineProps,
|
||||||
|
watch,
|
||||||
|
ref,
|
||||||
|
toRaw,
|
||||||
|
} from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { saveEquipment, initData } from "../request";
|
import { useModelSceneStore } from "@/store/modelSceneStore";
|
||||||
|
|
||||||
const emit = defineEmits(["cancel", "removeEquipment", "addEquipment"]);
|
const emit = defineEmits(["cancel", "removeEquipment", "addEquipment"]);
|
||||||
const params = defineProps([
|
const params = defineProps([
|
||||||
"pointNum",
|
"pointNum",
|
||||||
@@ -63,18 +72,17 @@ const params = defineProps([
|
|||||||
let p = ref(57);
|
let p = ref(57);
|
||||||
const equipment = ref(null);
|
const equipment = ref(null);
|
||||||
const equipmentType = ref(null);
|
const equipmentType = ref(null);
|
||||||
|
const modelStore = useModelSceneStore();
|
||||||
let maxConstructionLength = ref(0);
|
|
||||||
//计算锚点之间距离
|
//计算锚点之间距离
|
||||||
const pointDistance_str = computed(
|
const pointDistance_str = computed(
|
||||||
() =>
|
() =>
|
||||||
`${params.pointGap}米*${params.pointNum}=${(
|
`${parseInt(params.pointGap)}米*${params.pointNum}=${
|
||||||
params.pointGap * params.pointNum
|
parseInt(params.pointGap) * params.pointNum
|
||||||
).toFixed(1)}米`
|
}米`
|
||||||
);
|
);
|
||||||
|
|
||||||
// 请求数据模型
|
// 请求数据模型
|
||||||
const equipmentSetting = reactive({
|
const equipmentSetting = ref({
|
||||||
equipmentId: null,
|
equipmentId: null,
|
||||||
equipmentName: "",
|
equipmentName: "",
|
||||||
equipmentType: "", //设备类型
|
equipmentType: "", //设备类型
|
||||||
@@ -83,26 +91,24 @@ const equipmentSetting = reactive({
|
|||||||
typeKey: null, //设备选择(设备名称)
|
typeKey: null, //设备选择(设备名称)
|
||||||
});
|
});
|
||||||
function resetEquipmentSetting() {
|
function resetEquipmentSetting() {
|
||||||
equipmentSetting.equipmentId = null;
|
equipmentSetting.value.equipmentId = null;
|
||||||
equipmentSetting.equipmentName = "";
|
equipmentSetting.value.equipmentName = "";
|
||||||
equipmentSetting.equipmentType = "";
|
equipmentSetting.value.equipmentType = "";
|
||||||
equipmentSetting.position = "";
|
equipmentSetting.value.position = "";
|
||||||
equipmentSetting.threshold = "";
|
equipmentSetting.value.threshold = "";
|
||||||
equipmentSetting.typeKey = null;
|
equipmentSetting.value.typeKey = null;
|
||||||
equipment.value.reset();
|
equipment.value.reset();
|
||||||
equipmentType.value.reset();
|
equipmentType.value.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定选择的设备类型
|
// 绑定选择的设备类型
|
||||||
function handleTypeChange(optionItem) {
|
function handleTypeChange(optionItem) {
|
||||||
equipmentSetting.equipmentType = optionItem.value;
|
equipmentSetting.value.equipmentType = optionItem.value;
|
||||||
console.log("option", optionItem);
|
|
||||||
}
|
}
|
||||||
function handleEquipmentChange(equipmentItem) {
|
function handleEquipmentChange(equipmentItem) {
|
||||||
equipmentSetting.equipmentName = equipmentItem.label;
|
equipmentSetting.value.equipmentName = equipmentItem.label;
|
||||||
equipmentSetting.equipmentId = equipmentItem.equipmentId;
|
equipmentSetting.value.equipmentId = equipmentItem.equipmentId;
|
||||||
equipmentSetting.typeKey = equipmentItem.value;
|
equipmentSetting.value.typeKey = equipmentItem.value;
|
||||||
console.log("option", equipmentItem);
|
|
||||||
}
|
}
|
||||||
// 设备类型选项参数
|
// 设备类型选项参数
|
||||||
let options = reactive([{ label: "暂无数据", value: "" }]);
|
let options = reactive([{ label: "暂无数据", value: "" }]);
|
||||||
@@ -112,7 +118,7 @@ let options2 = reactive([{ equipmentId: 0, label: "暂无数据", value: "" }]);
|
|||||||
|
|
||||||
// 模拟生成option2
|
// 模拟生成option2
|
||||||
watch(
|
watch(
|
||||||
() => equipmentSetting.equipmentType,
|
() => equipmentSetting.value.equipmentType,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
console.log(params.tunnelId, newVal);
|
console.log(params.tunnelId, newVal);
|
||||||
equipment.value.reset();
|
equipment.value.reset();
|
||||||
@@ -164,19 +170,23 @@ function addEquipment() {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!equipmentSetting.equipmentType || !equipmentSetting.equipmentId) {
|
if (
|
||||||
|
!equipmentSetting.value.equipmentType ||
|
||||||
|
!equipmentSetting.value.equipmentId
|
||||||
|
) {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: "选项不能为空!",
|
message: "选项不能为空!",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
equipmentSetting.position = params.position;
|
equipmentSetting.value.position = params.position;
|
||||||
console.log("add", equipmentSetting);
|
console.log("add-", toRaw(equipmentSetting.value));
|
||||||
// 合法请求
|
// 合法请求
|
||||||
saveEquipment(equipmentSetting, params.pointGap)
|
modelStore
|
||||||
|
.addEquipment({ ...toRaw(equipmentSetting.value) }, params.pointGap)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
emit("addEquipment", equipmentSetting);
|
emit("addEquipment", equipmentSetting.value);
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: "添加成功!",
|
message: "添加成功!",
|
||||||
type: "success",
|
type: "success",
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { apiSaveEquipment, initSceneData } from "@/api/tunnelScene";
|
import { apiSaveEquipment, initSceneData } from "@/api/tunnelScene";
|
||||||
import { toRaw } from "vue";
|
import { toRaw } from "vue";
|
||||||
let modelEquipmentList = [];
|
let modelEquipmentList = [];
|
||||||
let originData = null;
|
|
||||||
// 初始化当前隧道数据并保存在本地
|
// 初始化当前隧道数据并保存在本地
|
||||||
export const initData = (tunnelId, otherInfo) => {
|
export const initData = (tunnelId, otherInfo) => {
|
||||||
|
modelEquipmentList = [];
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
initSceneData(tunnelId)
|
initSceneData(tunnelId)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
originData = toRaw(otherInfo);
|
|
||||||
if (res.data.tunnelThreeConfig) {
|
if (res.data.tunnelThreeConfig) {
|
||||||
modelEquipmentList = JSON.parse(res.data.tunnelThreeConfig);
|
modelEquipmentList = JSON.parse(res.data.tunnelThreeConfig);
|
||||||
|
resolve(modelEquipmentList);
|
||||||
}
|
}
|
||||||
console.log("test", modelEquipmentList, originData);
|
originData = toRaw(otherInfo);
|
||||||
resolve(modelEquipmentList);
|
|
||||||
})
|
})
|
||||||
.catch((Error) => {
|
.catch((Error) => {
|
||||||
ElMessage.error("场景初始化异常!");
|
ElMessage.error("设备初始化异常!");
|
||||||
reject();
|
reject();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -34,35 +34,31 @@ export const saveEquipment = (equipmentInfo, pointGap) => {
|
|||||||
tunnelName,
|
tunnelName,
|
||||||
} = originData;
|
} = originData;
|
||||||
let tempModelEquipmentList = [...modelEquipmentList]; //数组克隆
|
let tempModelEquipmentList = [...modelEquipmentList]; //数组克隆
|
||||||
tempModelEquipmentList.push(toRaw(equipmentInfo));
|
const obj = toRaw(equipmentInfo);
|
||||||
|
tempModelEquipmentList.push(obj);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log("equId", equipmentInfo.equipmentId);
|
apiSaveEquipment({
|
||||||
hasEquipment(equipmentInfo.equipmentId)
|
constructionLength: autoComputeConstructionLength(
|
||||||
? reject(3000)
|
tempModelEquipmentList,
|
||||||
: apiSaveEquipment({
|
pointGap
|
||||||
constructionLength: autoComputeConstructionLength(
|
),
|
||||||
tempModelEquipmentList,
|
isDefault,
|
||||||
pointGap
|
modelEquipmentList: tempModelEquipmentList,
|
||||||
),
|
remarks,
|
||||||
isDefault,
|
serialNumber,
|
||||||
modelEquipmentList: tempModelEquipmentList,
|
tunnelId,
|
||||||
remarks,
|
tunnelLength: totalLength,
|
||||||
serialNumber,
|
tunnelName,
|
||||||
tunnelId,
|
})
|
||||||
tunnelLength: totalLength,
|
.then((res) => {
|
||||||
tunnelName,
|
if (res?.code === 1000) {
|
||||||
})
|
modelEquipmentList = [...tempModelEquipmentList]; //存入本地
|
||||||
.then((res) => {
|
resolve();
|
||||||
if (res?.code === 1000) {
|
}
|
||||||
modelEquipmentList.push(toRaw(equipmentInfo)); //存入本地
|
})
|
||||||
resolve();
|
.catch((err) => {
|
||||||
}
|
reject();
|
||||||
console.log("add", res);
|
});
|
||||||
reject(res);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
reject();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,12 +130,3 @@ function autoComputeConstructionLength(modelEquipmentList, pointGap) {
|
|||||||
});
|
});
|
||||||
return maxLength;
|
return maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasEquipment(equipmentId) {
|
|
||||||
let result = false;
|
|
||||||
modelEquipmentList.forEach((item) => {
|
|
||||||
if (item.equipmentId === equipmentId) result = true;
|
|
||||||
});
|
|
||||||
console.log("add", modelEquipmentList);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,53 +1,122 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ref } from "vue";
|
import { ref, toRaw } from "vue";
|
||||||
import { saveEquipment, initSceneData } from "../api/tunnelScene";
|
import { apiSaveEquipment, initSceneData } from "../api/tunnelScene";
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
export const useModelSceneStore = defineStore("modelSceneData", () => {
|
export const useModelSceneStore = defineStore("modelSceneData", () => {
|
||||||
let equipmentList = ref([]);
|
let equipmentList = ref([]);
|
||||||
let allEditList = ref({});
|
let requestOtherConfig = ref({});
|
||||||
function initData(tunnelId = 1) {
|
|
||||||
initSceneData(tunnelId)
|
|
||||||
.then((res) => {
|
|
||||||
// equipmentList.value = res.data.tunnelThreeConfig;
|
|
||||||
console.log("初始化的数据");
|
|
||||||
})
|
|
||||||
.catch((Error) => {
|
|
||||||
ElMessage.error("场景初始化异常!");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//默认初始化1号隧道
|
|
||||||
function saveSceneData(data) {
|
|
||||||
equipmentList.value.push(data);
|
|
||||||
|
|
||||||
return saveEquipment(constructionLength, tunnelId, equipmentList.value);
|
function initModelData(tunnelId, otherConfig) {
|
||||||
}
|
|
||||||
// 删除设备
|
|
||||||
function deleteEquipment(constructionLength, tunnelId, EquipmentId) {
|
|
||||||
let temp = JSON.parse(equipmentList.value);
|
|
||||||
temp = temp.filter(
|
|
||||||
(item) => item.chooseEquipment.equipmentId != EquipmentId
|
|
||||||
);
|
|
||||||
equipmentList.value = JSON.stringify(temp);
|
|
||||||
return saveEquipment(constructionLength, tunnelId, equipmentList.value);
|
|
||||||
}
|
|
||||||
function getEquipmentList(tunnelId = 1) {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
handleSettingOtherConfig(otherConfig, requestOtherConfig);
|
||||||
initSceneData(tunnelId)
|
initSceneData(tunnelId)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
equipmentList.value = res.data.tunnelThreeConfig;
|
if (res.data.tunnelThreeConfig) {
|
||||||
resolve(JSON.parse(equipmentList.value));
|
equipmentList.value = JSON.parse(res.data.tunnelThreeConfig);
|
||||||
|
} else {
|
||||||
|
equipmentList.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(toRaw(equipmentList.value));
|
||||||
})
|
})
|
||||||
.catch((Error) => {
|
.catch((error) => {
|
||||||
// ElMessage.error("场景初始化异常!");
|
|
||||||
reject();
|
reject();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addEquipment(equipmentInfo, pointGap) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (hasEquipment(equipmentList.value, equipmentInfo.equipmentId)) {
|
||||||
|
reject(3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
equipmentList.value.push(equipmentInfo);
|
||||||
|
apiSaveEquipment({
|
||||||
|
constructionLength: autoComputeConstructionLength(
|
||||||
|
equipmentList.value,
|
||||||
|
pointGap
|
||||||
|
),
|
||||||
|
modelEquipmentList: toRaw(equipmentList.value),
|
||||||
|
...requestOtherConfig,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
console.log("res", res);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("res", err);
|
||||||
|
equipmentList.value.pop();
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 删除设备
|
||||||
|
function deleteEquipment(equipmentId, pointGap) {
|
||||||
|
let equipment = null;
|
||||||
|
equipmentList.value = equipmentList.value.filter((item) => {
|
||||||
|
if (item.equipmentId === equipmentId) {
|
||||||
|
equipment = item;
|
||||||
|
}
|
||||||
|
return item.equipmentId != equipmentId;
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
apiSaveEquipment({
|
||||||
|
constructionLength: autoComputeConstructionLength(
|
||||||
|
equipmentList.value,
|
||||||
|
pointGap
|
||||||
|
),
|
||||||
|
modelEquipmentList: toRaw(equipmentList.value),
|
||||||
|
...requestOtherConfig,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
equipmentList.value.push(equipment);
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
equipmentList,
|
initModelData,
|
||||||
initData,
|
addEquipment,
|
||||||
saveSceneData,
|
|
||||||
deleteEquipment,
|
deleteEquipment,
|
||||||
getEquipmentList,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleSettingOtherConfig(otherConfig, requestOtherConfig) {
|
||||||
|
requestOtherConfig.isDefault = otherConfig.isDefault;
|
||||||
|
requestOtherConfig.remarks = otherConfig.remarks;
|
||||||
|
requestOtherConfig.serialNumber = otherConfig.serialNumber;
|
||||||
|
requestOtherConfig.tunnelId = otherConfig.tunnelId;
|
||||||
|
requestOtherConfig.tunnelLength = otherConfig.totalLength;
|
||||||
|
requestOtherConfig.tunnelName = otherConfig.tunnelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoComputeConstructionLength(modelEquipmentList, pointGap) {
|
||||||
|
let maxLength = 0;
|
||||||
|
modelEquipmentList.forEach((item) => {
|
||||||
|
const length =
|
||||||
|
Number(
|
||||||
|
item.position.substring(
|
||||||
|
item.position.indexOf("_") + 1,
|
||||||
|
item.position.lastIndexOf("_")
|
||||||
|
) - 1
|
||||||
|
) * pointGap;
|
||||||
|
if (length > maxLength) {
|
||||||
|
maxLength = length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasEquipment(equipmentList, equipmentId) {
|
||||||
|
let result = false;
|
||||||
|
equipmentList.forEach((item) => {
|
||||||
|
if (item.equipmentId === equipmentId) result = true;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const router = useRouter();
|
|||||||
const tunnelId = reactive(router.currentRoute.value.params.tunnelId);
|
const tunnelId = reactive(router.currentRoute.value.params.tunnelId);
|
||||||
const userId = reactive(router.currentRoute.value.params.userId);
|
const userId = reactive(router.currentRoute.value.params.userId);
|
||||||
const type = reactive(router.currentRoute.value.params.type);
|
const type = reactive(router.currentRoute.value.params.type);
|
||||||
const store = useModelSceneStore();
|
const modelStore = useModelSceneStore();
|
||||||
const form = ref({
|
const form = ref({
|
||||||
tunnelName: "",
|
tunnelName: "",
|
||||||
serialNumber: "",
|
serialNumber: "",
|
||||||
@@ -70,7 +70,7 @@ const form = ref({
|
|||||||
});
|
});
|
||||||
const equipmentList = ref([]);
|
const equipmentList = ref([]);
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
equipmentList.value = await store.getEquipmentList();
|
// equipmentList.value = await store.getEquipmentList();
|
||||||
});
|
});
|
||||||
const getTunnel = () => {
|
const getTunnel = () => {
|
||||||
getTunnelDetail(tunnelId).then((res) => {
|
getTunnelDetail(tunnelId).then((res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user