123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- <template>
- <RightFillPano>
- <ui-group title="初始画面" borderBottom>
- <ui-group-option>
- <div class="init-pic" :class="{ disabled: isEdit }">
- <img :src="getFileUrl(setting!.cover)" class="init-puc-cover" />
- <div class="init-pic-set" @click="enterSetPic">设置</div>
- </div>
- </ui-group-option>
- </ui-group>
- <ui-group title="指北针">
- <template #icon>
- <ui-icon
- ctrl
- :type="setting?.openCompass ? 'eye-s' : 'eye-n'"
- @click="changeBack(setting!.back, setting!.backType, !setting!.openCompass, setting!.mapOpen, setting!.mapType,
- {scale: setting!.scale!, rotate: setting?.rotate!})"
- />
- </template>
- </ui-group>
- <ui-group title="地图" v-if="caseProject!.tmProject?.latlng">
- <template #icon>
- <ui-icon
- ctrl
- :type="setting?.mapOpen ? 'eye-s' : 'eye-n'"
- @click="changeBack(setting!.back, setting!.backType, setting!.openCompass, !setting!.mapOpen, setting!.mapType, {scale: setting!.scale!, rotate: setting?.rotate!})"
- />
- </template>
- <ui-group-option v-if="setting?.mapOpen">
- <ui-input
- type="select"
- width="100%"
- :options="[
- { label: '卫星地图', value: 'satellite' },
- { label: '矢量地图', value: 'standard' },
- ]"
- :modelValue="setting!.mapType"
- @update:modelValue="(e: string )=> changeBack(setting!.back, setting!.backType, setting!.openCompass, setting!.mapOpen, e, {scale: setting!.scale!, rotate: setting?.rotate!})"
- />
- </ui-group-option>
- </ui-group>
- <ui-group title="视角范围">
- <ui-group-option class="ant-modal-root">
- <Slider
- :value="setting!.fov || 70"
- :min="40"
- :step="1"
- :max="100"
- @update:value="(val: any) => changeFov(val)"
- />
- </ui-group-option>
- </ui-group>
- <ui-group title="设置背景">
- <ui-group-option>
- <div class="back-layout">
- <div
- v-for="back in settingResources"
- :key="back.resource"
- class="back-item"
- :class="{ [back.backType]: true, active: setting!.back === back.resource }"
- @click="setting!.back !== back.resource && changeBack(back.resource, back.backType, setting!.openCompass, setting!.mapOpen, setting!.mapType, {scale: setting!.scale!, rotate: setting?.rotate!})"
- >
- <img
- :src="back.covre || back.resource"
- v-if="back.backType === 'img' || back.backType === 'bimg'"
- />
- <i
- class="iconfont"
- :class="back.resource"
- v-else-if="back.backType === 'icon'"
- />
- <span :style="{ background: back.resource }" v-else></span>
- <p class="back-item-desc">
- {{
- (settingResourceTypeDesc[back.backType] &&
- settingResourceTypeDesc[back.backType] + "-") + back.name
- }}
- </p>
- <ui-button
- v-if="!back.sys"
- type="primary"
- class="del"
- @click.stop="delBack(back)"
- >删除</ui-button
- >
- </div>
- <ui-input
- class="input"
- preview
- accept=".jpg, .jpeg, .png"
- @update:modelValue="iconUpload"
- type="file"
- >
- <template v-slot:replace>
- <div class="back-item icon">
- <i class="iconfont icon-add" />
- </div>
- </template>
- </ui-input>
- </div>
- </ui-group-option>
- </ui-group>
- <Teleport
- to="#layout-app"
- v-if="
- setting?.backType === SettingResourceType.bottomImage &&
- $router.currentRoute.value.name === RoutesName.setting
- "
- >
- <div class="slider-demo-block ant-modal-root">
- 缩放
- <Slider
- :value="setting!.scale || 1"
- vertical
- :min="0.1"
- :step="0.01"
- :max="3"
- @update:value="(val: any) => changeBack(setting!.back, setting!.backType, setting!.openCompass, setting!.mapOpen, setting!.mapType, {scale: val, rotate: setting?.rotate!})"
- />
- </div>
- <div class="slider-demo-block ant-modal-root" style="margin-right: 60px">
- 旋转
- <Slider
- :value="setting!.rotate || 0"
- vertical
- :min="0.1"
- :step="0.01"
- :max="360"
- @update:value="(val: any) => changeBack(setting!.back, setting!.backType, setting!.openCompass, setting!.mapOpen, setting!.mapType, {scale: setting!.scale!, rotate: val})"
- />
- </div>
- </Teleport>
- </RightFillPano>
- <div class="edit-add-type" v-if="addTemp">
- <div class="edit-hot-item">
- <h3 class="edit-title">
- 背景图
- <ui-icon type="close" ctrl @click.stop="addTemp = undefined" class="edit-close" />
- </h3>
- <ui-input
- require
- class="input"
- width="100%"
- placeholder="请输入背景图名称标注"
- type="text"
- v-model="addTemp.name"
- maxlength="15"
- />
- <ui-input
- require
- class="input"
- width="100%"
- placeholder="请输入背景图名称标注"
- type="select"
- :options="options"
- v-model="addTemp.backType"
- maxlength="15"
- />
- <div class="edit-hot">
- <a @click="addBack">
- <ui-icon type="nav-edit" />
- 确定
- </a>
- </div>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { RightFillPano } from "@/layout";
- import {
- enterEdit,
- enterOld,
- setting,
- isEdit,
- updataSetting,
- caseProject,
- } from "@/store";
- import { ref } from "vue";
- import { togetherCallback, getFileUrl, loadPack } from "@/utils";
- import { showRightPanoStack, showRightCtrlPanoStack } from "@/env";
- import { sdk, setBackdrop, setMap } from "@/sdk";
- import {
- delSettingResource,
- fetchSettingResources,
- settingResources,
- settingResourceTypeDesc,
- } from "@/api/setting-resource";
- import { uploadFile } from "@/api";
- import { SettingResource, addSettingResource } from "@/api/setting-resource";
- import { SettingResourceType } from "@/api/setting-resource";
- import { Dialog } from "bill/index";
- import { Slider } from "ant-design-vue";
- import { RoutesName } from "@/router";
- fetchSettingResources();
- const addBack = async () => {
- if (!addTemp.value!.name.trim()) {
- Dialog.alert("请输入名称");
- return;
- }
- await addSettingResource(addTemp.value!);
- addTemp.value = undefined;
- await fetchSettingResources();
- };
- const enterSetPic = () => {
- enterEdit(
- togetherCallback([
- showRightPanoStack.push(ref(false)),
- showRightCtrlPanoStack.push(ref(false)),
- ])
- );
- enterOld(async () => {
- const dataURL = await sdk.screenshot(300, 150);
- const res = await fetch(dataURL);
- const blob = await res.blob();
- setting.value = {
- ...setting.value!,
- cover: { url: dataURL, blob },
- pose: sdk.getPose(),
- };
- await updataSetting();
- });
- };
- const initBack = setting.value!.back;
- const initType = setting.value!.backType;
- const initOpenCompass = setting.value!.openCompass;
- const initopenMap = setting.value!.mapOpen;
- const initmapType = setting.value!.mapType;
- const initScale = setting.value!.scale;
- const initRotate = setting.value!.rotate;
- let isFirst = true;
- const changeBack = (
- back: string,
- type: SettingResourceType,
- openCompass: boolean,
- openMap: boolean,
- mapType: string,
- tb: { scale: number; rotate: number } = { scale: 1, rotate: 0 }
- ) => {
- if (type === SettingResourceType.map && !caseProject.value!.tmProject?.latlng) {
- Dialog.alert("当前案件没绑定经纬度,无法开启地图功能");
- return;
- }
- setting.value!.back = back;
- setting.value!.backType = type;
- setting.value!.openCompass = openCompass;
- setting.value!.mapOpen = openMap;
- setting.value!.mapType = mapType;
- setting.value!.scale = tb.scale;
- setting.value!.rotate = tb.rotate;
- setBackdrop(back, type, tb);
- setMap(openMap, mapType);
- (document.querySelector("#direction") as HTMLDivElement)!.style.display = openCompass
- ? "block"
- : "none";
- if (isFirst) {
- let isSave = false;
- isFirst = false;
- enterEdit(() => {
- if (!isSave) {
- setting.value!.back = initBack;
- setting.value!.backType = initType;
- setting.value!.openCompass = initOpenCompass;
- setting.value!.mapOpen = initopenMap;
- setting.value!.mapType = initmapType;
- setting.value!.scale = initScale;
- setting.value!.rotate = initRotate;
- setBackdrop(initBack, initType, { scale: initScale, rotate: initRotate });
- setMap(initopenMap, initmapType);
- (document.querySelector(
- "#direction"
- ) as HTMLDivElement)!.style.display = initOpenCompass ? "block" : "none";
- }
- isFirst = true;
- });
- enterOld(async () => {
- isSave = true;
- await loadPack(updataSetting());
- });
- }
- };
- const delBack = (() => {
- let isFirst = true;
- let oldResources: SettingResource[];
- let dels: SettingResource[] = [];
- return (back: SettingResource) => {
- if (setting.value?.back === back.resource) {
- changeBack(
- settingResources.value[0].resource,
- settingResources.value[0].backType,
- setting!.value.openCompass,
- setting!.value.mapOpen,
- setting!.value.mapType,
- { scale: setting!.value.scale!, rotate: setting!.value.rotate! }
- );
- }
- if (isFirst) {
- dels = [];
- oldResources = [...settingResources.value];
- }
- const ndx = settingResources.value.indexOf(back);
- if (~ndx) {
- settingResources.value.splice(ndx, 1);
- dels.push(back);
- }
- if (isFirst) {
- let isSave = false;
- isFirst = false;
- enterEdit(() => {
- if (!isSave) {
- settingResources.value = oldResources;
- }
- isFirst = true;
- });
- enterOld(async () => {
- isSave = true;
- await loadPack(Promise.all(dels.map(delSettingResource)));
- });
- }
- };
- })();
- const changeFov = (() => {
- let isFirst = true;
- let initFov: number;
- return (fov: number) => {
- if (isFirst) {
- initFov = setting.value!.fov;
- }
- setting.value!.fov = fov;
- if (isFirst) {
- let isSave = false;
- isFirst = false;
- enterEdit(() => {
- if (!isSave) {
- setting.value!.fov = initFov;
- }
- isFirst = true;
- });
- enterOld(async () => {
- isSave = true;
- await loadPack(updataSetting());
- });
- }
- };
- })();
- const options = [
- {
- value: SettingResourceType.envImage,
- label: settingResourceTypeDesc[SettingResourceType.envImage],
- },
- {
- value: SettingResourceType.bottomImage,
- label: settingResourceTypeDesc[SettingResourceType.bottomImage],
- },
- ];
- const addTemp = ref<Omit<SettingResource, "id">>();
- const iconUpload = async (data: any) => {
- addTemp.value = {
- resource: await uploadFile({ blob: data.file as any, url: "" }),
- name: "",
- backType: SettingResourceType.envImage,
- };
- };
- </script>
- <style scoped lang="scss">
- .init-pic {
- height: 150px;
- border-radius: 4px;
- overflow: hidden;
- position: relative;
- }
- .init-puc-cover {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- .init-pic-set {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- background-color: rgba(0, 0, 0, 0.5);
- font-size: 12px;
- color: #fff;
- line-height: 32px;
- z-index: 1;
- text-align: center;
- cursor: pointer;
- }
- .back-layout {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 20px;
- }
- .back-item {
- position: relative;
- > span,
- .iconfont,
- img {
- display: block;
- height: 88px;
- cursor: pointer;
- outline: 2px solid transparent;
- transition: all 0.3s;
- border-radius: 4px;
- width: 88px;
- object-fit: cover;
- }
- .del {
- position: absolute;
- top: 64px;
- height: 24px;
- left: 0;
- right: 0;
- }
- .iconfont {
- display: flex;
- align-items: center;
- justify-content: center;
- color: #525252;
- font-size: 32px;
- }
- img {
- object-fit: cover;
- }
- &.active {
- > span,
- .iconfont,
- img {
- outline-color: #00c8af;
- }
- }
- }
- .back-item-desc {
- font-size: 14px;
- color: #fff;
- margin-top: 10px;
- text-align: center;
- }
- .edit-add-type {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, 0.3);
- backdrop-filter: blur(4px);
- z-index: 2000;
- padding: 20px;
- overflow-y: auto;
- .edit-hot-item {
- margin: 100px auto 20px;
- width: 400px;
- padding: 20px;
- background: rgba(27, 27, 28, 0.8);
- box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
- border-radius: 4px;
- .input {
- margin-bottom: 10px;
- }
- }
- }
- .edit-hot {
- margin-top: 20px;
- text-align: right;
- span {
- font-size: 14px;
- color: rgba(255, 255, 255, 0.6);
- cursor: pointer;
- }
- }
- .edit-close {
- position: absolute;
- cursor: pointer;
- top: calc((100% - 18px) / 2);
- right: 0;
- transform: translateY(-50%);
- }
- .edit-title {
- padding-bottom: 18px;
- margin-bottom: 18px;
- position: relative;
- &::after {
- content: "";
- position: absolute;
- left: -20px;
- right: -20px;
- height: 1px;
- bottom: 0;
- background-color: rgba(255, 255, 255, 0.16);
- }
- }
- .slider-demo-block {
- position: absolute;
- right: calc(var(--editor-menu-right) + var(--editor-toolbox-width)) !important;
- top: 50%;
- transform: translateY(-50%);
- margin-right: 20px;
- z-index: 99;
- height: 300px;
- }
- </style>
|