123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- <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>
- <a class="back-item-desc">
- {{
- (settingResourceTypeDesc[back.backType] &&
- settingResourceTypeDesc[back.backType] + "-") + back.name
- }}
- </a>
- <ui-icon
- v-if="!back.sys"
- type="close"
- class="del"
- @click.stop="delBack(back)"
- />
- </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-group">
- <div class="slider-demo-block ant-modal-root">
- <span> 缩放</span>
- <Slider
- :value="setting!.scale || 1"
- :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">
- <span> 旋转</span>
- <Slider
- :value="setting!.rotate || 0"
- :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>
- </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,
- createTemploraryID,
- save,
- } from "@/store";
- import { ref } from "vue";
- import { togetherCallback, getFileUrl, loadPack, asyncTimeout } 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 = async (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! }
- );
- await asyncTimeout(100);
- await save();
- }
- const ndx = settingResources.value.indexOf(back);
- if (~ndx) {
- settingResources.value.splice(ndx, 1);
- }
- await delSettingResource(back);
- };
- const delBack1 = (() => {
- 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<SettingResource>();
- 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;
- background: #ef4347;
- width: 20px;
- height: 20px;
- top: -10px;
- right: -10px;
- opacity: 0;
- // opacity: 1;
- border-radius: 50%;
- &.iconfont {
- font-size: 10px;
- color: #fff;
- }
- &:hover {
- opacity: 1 !important;
- }
- }
- &:hover .del {
- opacity: 0.9;
- }
- .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;
- }
- }
- }
- :deep(.back-item .del.iconfont) {
- outline-style: none;
- }
- .back-item-desc {
- font-size: 14px;
- color: #fff;
- margin-top: 10px;
- display: block;
- text-align: center;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- -o-text-overflow: ellipsis;
- white-space: nowrap; //文本不会换行
- width: 88px;
- }
- .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-group {
- position: absolute;
- z-index: 10000;
- padding: 8px 24px;
- background-color: rgba(0, 0, 0, 0.3);
- left: 50%;
- border-radius: 5px;
- bottom: 20px;
- transform: translateX(-50%);
- margin-right: 20px;
- z-index: 99;
- // height: 300px;
- width: 386px;
- display: flex;
- flex-wrap: wrap;
- flex-direction: row;
- }
- .slider-demo-block {
- flex: 1 1 100%;
- display: inline-flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- span {
- padding-right: 10px;
- }
- .ant-slider {
- flex: 1;
- }
- }
- :global(.ant-modal-root .ant-slider-track) {
- background-color: #00c8af;
- }
- :global(.ant-modal-root .ant-slider:hover .ant-slider-track) {
- background-color: #00c8af;
- }
- :global(.ant-modal-root .ant-slider-handle) {
- border: solid 2px #00c8af !important;
- }
- :global(.ant-modal-root .ant-slider-handle.ant-tooltip-open) {
- border-color: #00c8af;
- }
- :global(
- .ant-modal-root .ant-slider-handle:focus,
- .ant-modal-root .ant-slider-handle:hover,
- .ant-modal-root .ant-slider-handle:active
- ) {
- border: solid 2px #03ad99 !important;
- box-shadow: none;
- }
- </style>
|