|
@@ -1,111 +1,115 @@
|
|
<template>
|
|
<template>
|
|
<div class="video">
|
|
<div class="video">
|
|
<div class="overflow">
|
|
<div class="overflow">
|
|
- <ui-icon
|
|
|
|
- ctrl
|
|
|
|
- :type="isScenePlayIng ? 'pause' : 'preview'"
|
|
|
|
- :disabled="!paths.length"
|
|
|
|
|
|
+ <ui-icon
|
|
|
|
+ ctrl
|
|
|
|
+ :type="isScenePlayIng ? 'pause' : 'preview'"
|
|
|
|
+ :disabled="!paths.length"
|
|
@click="play"
|
|
@click="play"
|
|
/>
|
|
/>
|
|
- <ui-button
|
|
|
|
- type="primary"
|
|
|
|
- @click="addPath"
|
|
|
|
- width="200px"
|
|
|
|
|
|
+ <ui-button
|
|
|
|
+ type="primary"
|
|
|
|
+ @click="addPath"
|
|
|
|
+ width="200px"
|
|
:class="{ disabled: isScenePlayIng }"
|
|
:class="{ disabled: isScenePlayIng }"
|
|
>
|
|
>
|
|
- 添加视角
|
|
|
|
|
|
+ {{ $t("sys.guide.addPath") }}
|
|
</ui-button>
|
|
</ui-button>
|
|
</div>
|
|
</div>
|
|
<div class="info" v-if="paths.length">
|
|
<div class="info" v-if="paths.length">
|
|
<div class="meta">
|
|
<div class="meta">
|
|
<div class="length">
|
|
<div class="length">
|
|
- <span>视频时长</span>{{paths.reduce((t, c) => t + c.time, 0).toFixed(1)}}s
|
|
|
|
|
|
+ <span>{{ $t("sys.guide.length") }}</span
|
|
|
|
+ >{{ paths.reduce((t, c) => t + c.time, 0).toFixed(1) }}s
|
|
</div>
|
|
</div>
|
|
- <div
|
|
|
|
- class="fun-ctrl clear"
|
|
|
|
- @click="deleteAll"
|
|
|
|
|
|
+ <div
|
|
|
|
+ class="fun-ctrl clear"
|
|
|
|
+ @click="deleteAll"
|
|
:class="{ disabled: isScenePlayIng }"
|
|
:class="{ disabled: isScenePlayIng }"
|
|
>
|
|
>
|
|
<ui-icon type="del" />
|
|
<ui-icon type="del" />
|
|
- <span>清空画面</span>
|
|
|
|
|
|
+ <span>{{ $t("sys.guide.clear") }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="photo-list" ref="listVm">
|
|
<div class="photo-list" ref="listVm">
|
|
<template v-for="(path, i) in paths" :key="path.id">
|
|
<template v-for="(path, i) in paths" :key="path.id">
|
|
- <div
|
|
|
|
- class="photo"
|
|
|
|
|
|
+ <div
|
|
|
|
+ class="photo"
|
|
:class="{ active: current === path, disabled: isScenePlayIng }"
|
|
:class="{ active: current === path, disabled: isScenePlayIng }"
|
|
@click="changeCurrent(path)"
|
|
@click="changeCurrent(path)"
|
|
>
|
|
>
|
|
- <ui-icon
|
|
|
|
- type="del"
|
|
|
|
- ctrl
|
|
|
|
- @click.stop="deletePath(path)"
|
|
|
|
- :class="{ disabled: isScenePlayIng }"
|
|
|
|
|
|
+ <ui-icon
|
|
|
|
+ type="del"
|
|
|
|
+ ctrl
|
|
|
|
+ @click.stop="deletePath(path)"
|
|
|
|
+ :class="{ disabled: isScenePlayIng }"
|
|
/>
|
|
/>
|
|
<img :src="getResource(getFileUrl(path.cover))" />
|
|
<img :src="getResource(getFileUrl(path.cover))" />
|
|
</div>
|
|
</div>
|
|
<div class="set-phone-attr" v-if="i !== paths.length - 1">
|
|
<div class="set-phone-attr" v-if="i !== paths.length - 1">
|
|
- <ui-input
|
|
|
|
- type="number"
|
|
|
|
- width="54px"
|
|
|
|
|
|
+ <ui-input
|
|
|
|
+ type="number"
|
|
|
|
+ width="54px"
|
|
height="26px"
|
|
height="26px"
|
|
- :modelValue="path.speed"
|
|
|
|
|
|
+ :modelValue="path.speed"
|
|
@update:modelValue="(val: number) => updatePathInfo(i, { speed: val })"
|
|
@update:modelValue="(val: number) => updatePathInfo(i, { speed: val })"
|
|
- :ctrl="false"
|
|
|
|
- :min="0.1"
|
|
|
|
|
|
+ :ctrl="false"
|
|
|
|
+ :min="0.1"
|
|
:max="10"
|
|
:max="10"
|
|
>
|
|
>
|
|
<template #icon><span>m/s</span></template>
|
|
<template #icon><span>m/s</span></template>
|
|
</ui-input>
|
|
</ui-input>
|
|
- <ui-input
|
|
|
|
- type="number"
|
|
|
|
- width="54px"
|
|
|
|
- height="26px"
|
|
|
|
- v-model="path.time"
|
|
|
|
|
|
+ <ui-input
|
|
|
|
+ type="number"
|
|
|
|
+ width="54px"
|
|
|
|
+ height="26px"
|
|
|
|
+ v-model="path.time"
|
|
@update:modelValue="(val: number) => updatePathInfo(i, { time: val })"
|
|
@update:modelValue="(val: number) => updatePathInfo(i, { time: val })"
|
|
- :ctrl="false"
|
|
|
|
- :min="0.1"
|
|
|
|
- :max="20"
|
|
|
|
|
|
+ :ctrl="false"
|
|
|
|
+ :min="0.1"
|
|
|
|
+ :max="20"
|
|
class="time"
|
|
class="time"
|
|
>
|
|
>
|
|
<template #icon><span class="time">s</span></template>
|
|
<template #icon><span class="time">s</span></template>
|
|
</ui-input>
|
|
</ui-input>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
-
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
- <p class="un-video" v-else>暂无导览</p>
|
|
|
|
|
|
+ <p class="un-video" v-else>{{ $t("sys.guide.un") }}</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
-import { loadPack, togetherCallback, getFileUrl, asyncTimeout } from '@/utils'
|
|
|
|
-import { sdk, playSceneGuide, pauseSceneGuide, isScenePlayIng } from '@/sdk'
|
|
|
|
-import { createGuidePath, isTemploraryID, useAutoSetMode, guides } from '@/store'
|
|
|
|
-import { Dialog, Message } from 'bill/index'
|
|
|
|
-import { useViewStack } from '@/hook'
|
|
|
|
-import { nextTick, ref, toRaw, watchEffect } from 'vue'
|
|
|
|
-import { showRightPanoStack, showLeftCtrlPanoStack, showLeftPanoStack, showRightCtrlPanoStack, getResource } from '@/env'
|
|
|
|
|
|
+import { loadPack, togetherCallback, getFileUrl, asyncTimeout } from "@/utils";
|
|
|
|
+import { sdk, playSceneGuide, pauseSceneGuide, isScenePlayIng } from "@/sdk";
|
|
|
|
+import { createGuidePath, isTemploraryID, useAutoSetMode, guides } from "@/store";
|
|
|
|
+import { Dialog, Message } from "bill/index";
|
|
|
|
+import { useViewStack } from "@/hook";
|
|
|
|
+import { nextTick, ref, toRaw, watchEffect } from "vue";
|
|
|
|
+import {
|
|
|
|
+ showRightPanoStack,
|
|
|
|
+ showLeftCtrlPanoStack,
|
|
|
|
+ showLeftPanoStack,
|
|
|
|
+ showRightCtrlPanoStack,
|
|
|
|
+ getResource,
|
|
|
|
+} from "@/env";
|
|
|
|
|
|
-import type { Guide, GuidePaths, GuidePath } from '@/store'
|
|
|
|
-import type { CalcPathProps } from '@/sdk'
|
|
|
|
|
|
+import type { Guide, GuidePaths, GuidePath } from "@/store";
|
|
|
|
+import type { CalcPathProps } from "@/sdk";
|
|
|
|
+import { ui18n } from "@/lang";
|
|
|
|
|
|
-const props = defineProps< { data: Guide }>()
|
|
|
|
-const paths = ref<GuidePaths>(props.data.paths)
|
|
|
|
-const current = ref<GuidePath>(paths.value[0])
|
|
|
|
|
|
+const props = defineProps<{ data: Guide }>();
|
|
|
|
+const paths = ref<GuidePaths>(props.data.paths);
|
|
|
|
+const current = ref<GuidePath>(paths.value[0]);
|
|
|
|
|
|
const updatePathInfo = (index: number, calcInfo: CalcPathProps[1]) => {
|
|
const updatePathInfo = (index: number, calcInfo: CalcPathProps[1]) => {
|
|
- const info = sdk.calcPathInfo(
|
|
|
|
- paths.value.slice(index, index + 2) as any,
|
|
|
|
- calcInfo
|
|
|
|
- )
|
|
|
|
- Object.assign(paths.value[index], info)
|
|
|
|
-}
|
|
|
|
|
|
+ const info = sdk.calcPathInfo(paths.value.slice(index, index + 2) as any, calcInfo);
|
|
|
|
+ Object.assign(paths.value[index], info);
|
|
|
|
+};
|
|
|
|
|
|
-useViewStack(() =>
|
|
|
|
|
|
+useViewStack(() =>
|
|
togetherCallback([
|
|
togetherCallback([
|
|
showRightPanoStack.push(ref(false)),
|
|
showRightPanoStack.push(ref(false)),
|
|
showLeftCtrlPanoStack.push(ref(false)),
|
|
showLeftCtrlPanoStack.push(ref(false)),
|
|
@@ -114,87 +118,85 @@ useViewStack(() =>
|
|
])
|
|
])
|
|
);
|
|
);
|
|
|
|
|
|
-
|
|
|
|
useAutoSetMode(paths, {
|
|
useAutoSetMode(paths, {
|
|
- save() {
|
|
|
|
- }
|
|
|
|
-})
|
|
|
|
|
|
+ save() {},
|
|
|
|
+});
|
|
|
|
|
|
const addPath = () => {
|
|
const addPath = () => {
|
|
loadPack(async () => {
|
|
loadPack(async () => {
|
|
- const dataURL = await sdk.screenshot(260, 160)
|
|
|
|
- const res = await fetch(dataURL)
|
|
|
|
- const blob = await res.blob()
|
|
|
|
|
|
+ const dataURL = await sdk.screenshot(260, 160);
|
|
|
|
+ const res = await fetch(dataURL);
|
|
|
|
+ const blob = await res.blob();
|
|
|
|
|
|
- const pose = sdk.getPose()
|
|
|
|
- const index = paths.value.indexOf(current.value) + 1
|
|
|
|
- const path: GuidePath = createGuidePath({
|
|
|
|
- ...pose,
|
|
|
|
- cover: { url: dataURL, blob }
|
|
|
|
- })
|
|
|
|
- paths.value.splice(index, 0, path)
|
|
|
|
- current.value = path
|
|
|
|
|
|
+ const pose = sdk.getPose();
|
|
|
|
+ const index = paths.value.indexOf(current.value) + 1;
|
|
|
|
+ const path: GuidePath = createGuidePath({
|
|
|
|
+ ...pose,
|
|
|
|
+ cover: { url: dataURL, blob },
|
|
|
|
+ });
|
|
|
|
+ paths.value.splice(index, 0, path);
|
|
|
|
+ current.value = path;
|
|
if (paths.value.length > 1) {
|
|
if (paths.value.length > 1) {
|
|
- const index = paths.value.length - 2
|
|
|
|
- updatePathInfo(index, { time: 3 })
|
|
|
|
|
|
+ const index = paths.value.length - 2;
|
|
|
|
+ updatePathInfo(index, { time: 3 });
|
|
}
|
|
}
|
|
- })
|
|
|
|
-}
|
|
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
|
|
const deletePath = async (path: GuidePath, fore: boolean = false) => {
|
|
const deletePath = async (path: GuidePath, fore: boolean = false) => {
|
|
- if (fore || (await Dialog.confirm('确定要删除此画面吗?'))) {
|
|
|
|
- const index = paths.value.indexOf(path)
|
|
|
|
|
|
+ if (fore || (await Dialog.confirm(ui18n.t("sys.guide.delConfirm")))) {
|
|
|
|
+ const index = paths.value.indexOf(path);
|
|
if (~index) {
|
|
if (~index) {
|
|
- paths.value.splice(index, 1)
|
|
|
|
|
|
+ paths.value.splice(index, 1);
|
|
}
|
|
}
|
|
if (path === current.value) {
|
|
if (path === current.value) {
|
|
- current.value = paths.value[index + (index === 0 ? 0 : -1)]
|
|
|
|
|
|
+ current.value = paths.value[index + (index === 0 ? 0 : -1)];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
+};
|
|
|
|
|
|
const deleteAll = async () => {
|
|
const deleteAll = async () => {
|
|
- if (await Dialog.confirm('确定要清空画面吗?')) {
|
|
|
|
|
|
+ if (await Dialog.confirm(ui18n.t("sys.guide.delAllConfirm"))) {
|
|
while (paths.value.length) {
|
|
while (paths.value.length) {
|
|
- deletePath(paths.value[0], true)
|
|
|
|
|
|
+ deletePath(paths.value[0], true);
|
|
}
|
|
}
|
|
- current.value = paths.value[0]
|
|
|
|
|
|
+ current.value = paths.value[0];
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
+};
|
|
|
|
|
|
const changeCurrent = (path: GuidePath) => {
|
|
const changeCurrent = (path: GuidePath) => {
|
|
- sdk.comeTo({ dur: 300, ...path })
|
|
|
|
- current.value = path
|
|
|
|
-}
|
|
|
|
|
|
+ sdk.comeTo({ dur: 300, ...path });
|
|
|
|
+ current.value = path;
|
|
|
|
+};
|
|
|
|
|
|
const play = async () => {
|
|
const play = async () => {
|
|
if (isScenePlayIng.value) {
|
|
if (isScenePlayIng.value) {
|
|
- pauseSceneGuide()
|
|
|
|
|
|
+ pauseSceneGuide();
|
|
} else {
|
|
} else {
|
|
- changeCurrent(paths.value[0])
|
|
|
|
- await asyncTimeout(400)
|
|
|
|
|
|
+ changeCurrent(paths.value[0]);
|
|
|
|
+ await asyncTimeout(400);
|
|
playSceneGuide(toRaw(paths.value), (index) => {
|
|
playSceneGuide(toRaw(paths.value), (index) => {
|
|
- console.log('guide', index)
|
|
|
|
- current.value = paths.value[index - 1]
|
|
|
|
- })
|
|
|
|
|
|
+ console.log("guide", index);
|
|
|
|
+ current.value = paths.value[index - 1];
|
|
|
|
+ });
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
+};
|
|
|
|
|
|
-const listVm = ref<HTMLDivElement>()
|
|
|
|
|
|
+const listVm = ref<HTMLDivElement>();
|
|
watchEffect(async () => {
|
|
watchEffect(async () => {
|
|
- const index = paths.value.indexOf(current.value)
|
|
|
|
|
|
+ const index = paths.value.indexOf(current.value);
|
|
if (~index && listVm.value) {
|
|
if (~index && listVm.value) {
|
|
- await nextTick()
|
|
|
|
- const scrollWidth = listVm.value.scrollWidth / paths.value.length
|
|
|
|
- const centerWidth = listVm.value.offsetWidth / 2
|
|
|
|
- const offsetLeft = scrollWidth * index - centerWidth
|
|
|
|
|
|
+ await nextTick();
|
|
|
|
+ const scrollWidth = listVm.value.scrollWidth / paths.value.length;
|
|
|
|
+ const centerWidth = listVm.value.offsetWidth / 2;
|
|
|
|
+ const offsetLeft = scrollWidth * index - centerWidth;
|
|
|
|
|
|
listVm.value.scroll({
|
|
listVm.value.scroll({
|
|
left: offsetLeft,
|
|
left: offsetLeft,
|
|
top: 0,
|
|
top: 0,
|
|
- })
|
|
|
|
|
|
+ });
|
|
}
|
|
}
|
|
-})
|
|
|
|
|
|
+});
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
@@ -219,11 +221,11 @@ watchEffect(async () => {
|
|
|
|
|
|
.meta {
|
|
.meta {
|
|
font-size: 12px;
|
|
font-size: 12px;
|
|
- border-bottom: 1px solid rgba(255,255,255,.6);
|
|
|
|
|
|
+ border-bottom: 1px solid rgba(255, 255, 255, 0.6);
|
|
padding: 10px 20px;
|
|
padding: 10px 20px;
|
|
display: flex;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
-
|
|
|
|
|
|
+
|
|
.length span {
|
|
.length span {
|
|
margin-right: 10px;
|
|
margin-right: 10px;
|
|
}
|
|
}
|
|
@@ -238,7 +240,6 @@ watchEffect(async () => {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
.photo-list {
|
|
.photo-list {
|
|
padding: 10px 20px 20px;
|
|
padding: 10px 20px 20px;
|
|
overflow-x: auto;
|
|
overflow-x: auto;
|
|
@@ -254,8 +255,8 @@ watchEffect(async () => {
|
|
|
|
|
|
&::before,
|
|
&::before,
|
|
&::after {
|
|
&::after {
|
|
- content: '';
|
|
|
|
- color: rgba(255,255,255,.6);
|
|
|
|
|
|
+ content: "";
|
|
|
|
+ color: rgba(255, 255, 255, 0.6);
|
|
position: absolute;
|
|
position: absolute;
|
|
top: 50%;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
transform: translateY(-50%);
|
|
@@ -274,12 +275,11 @@ watchEffect(async () => {
|
|
border-left: 7px solid currentColor;
|
|
border-left: 7px solid currentColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
.photo {
|
|
.photo {
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
flex: none;
|
|
flex: none;
|
|
position: relative;
|
|
position: relative;
|
|
-
|
|
|
|
|
|
|
|
&.active {
|
|
&.active {
|
|
outline: 2px solid var(--colors-primary-base);
|
|
outline: 2px solid var(--colors-primary-base);
|
|
@@ -292,8 +292,8 @@ watchEffect(async () => {
|
|
width: 24px;
|
|
width: 24px;
|
|
font-size: 12px;
|
|
font-size: 12px;
|
|
height: 24px;
|
|
height: 24px;
|
|
- background-color: rgba(0,0,0,0.6);
|
|
|
|
- color: rgba(255,255,255,.6);
|
|
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.6);
|
|
|
|
+ color: rgba(255, 255, 255, 0.6);
|
|
display: flex;
|
|
display: flex;
|
|
align-items: center;
|
|
align-items: center;
|
|
justify-content: center;
|
|
justify-content: center;
|
|
@@ -301,7 +301,6 @@ watchEffect(async () => {
|
|
border-radius: 50%;
|
|
border-radius: 50%;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
img {
|
|
img {
|
|
width: 230px;
|
|
width: 230px;
|
|
height: 160px;
|
|
height: 160px;
|
|
@@ -314,7 +313,7 @@ watchEffect(async () => {
|
|
height: 100px;
|
|
height: 100px;
|
|
line-height: 100px;
|
|
line-height: 100px;
|
|
text-align: center;
|
|
text-align: center;
|
|
- color: rgba(255,255,255,0.6);
|
|
|
|
|
|
+ color: rgba(255, 255, 255, 0.6);
|
|
font-size: 1.2em;
|
|
font-size: 1.2em;
|
|
}
|
|
}
|
|
</style>
|
|
</style>
|
|
@@ -339,4 +338,4 @@ watchEffect(async () => {
|
|
text-align: right;
|
|
text-align: right;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-</style>
|
|
|
|
|
|
+</style>
|