264 lines
9.4 KiB
Vue
264 lines
9.4 KiB
Vue
<script setup lang="ts">
|
|
import {ref, onMounted, reactive, nextTick} from "vue";
|
|
import {Close} from '@vicons/carbon';
|
|
import {t} from "@/language";
|
|
import {fetchSceneExampleList} from "@/http/api/sceneExample";
|
|
import ProjectInfoForm from "@/components/project/ProjectInfoForm.vue";
|
|
import {App,defaultProjectInfo} from "@astral3d/engine";
|
|
import CesiumConfig from "@/components/project/CesiumConfig.vue";
|
|
import {fetchAddScene} from "@/http/api/scenes";
|
|
import EsTip from "@/components/es/EsTip.vue";
|
|
import {getServiceStaticFile} from "@/utils/common/file";
|
|
|
|
withDefaults(defineProps<{
|
|
visible: boolean
|
|
}>(), {
|
|
visible: false
|
|
})
|
|
const emits = defineEmits(['update:visible', "refresh"]);
|
|
|
|
const projectInfoFormRef = ref();
|
|
const cesiumConfigRef = ref();
|
|
|
|
const spin = ref(false);
|
|
|
|
const settingTabsValue = ref("project");
|
|
const currentType = ref<"Web3D" | "WebGIS">("Web3D");
|
|
const currentExample = ref<ISceneFetchData | null>(null);
|
|
const typeList: { name: string, type: "Web3D" | "WebGIS", img: string,isDev:boolean }[] = [
|
|
{name: "Web3D", type: "Web3D", img: "/static/images/placeholder/Web3D.jpg",isDev:false},
|
|
// {name: "WebGIS", type: "WebGIS", img: "/static/images/placeholder/WebGIS.jpg",isDev:true},
|
|
];
|
|
const exampleList = reactive<{
|
|
Web3D: ISceneFetchData[],
|
|
WebGIS: ISceneFetchData[]
|
|
}>({
|
|
Web3D: [],
|
|
WebGIS: []
|
|
})
|
|
|
|
async function getExampleScene() {
|
|
const res = await fetchSceneExampleList({
|
|
limit: 1000
|
|
})
|
|
if (res.data === null) return;
|
|
|
|
res.data.items.forEach(item => {
|
|
switch (item.projectType) {
|
|
case 0:
|
|
exampleList.Web3D.push(item);
|
|
break;
|
|
case 1:
|
|
exampleList.Web3D.push(item);
|
|
break;
|
|
}
|
|
})
|
|
}
|
|
|
|
// 切换场景类型
|
|
function handleChangeProjectType(type) {
|
|
if(type.isDev){
|
|
window.$message?.warning(`${type.name}: ${t("prompt['Related modules are under development, unstable versions.']")}`)
|
|
}
|
|
|
|
currentType.value = type.type;
|
|
|
|
// 选中空场景
|
|
currentExample.value = null;
|
|
App.project.setKey("sceneInfo",defaultProjectInfo().sceneInfo);
|
|
|
|
settingTabsValue.value = '';
|
|
nextTick(() => {
|
|
settingTabsValue.value = 'project';
|
|
})
|
|
}
|
|
|
|
// 选中示例模板
|
|
function handleSelectExample(example: ISceneFetchData | null) {
|
|
currentExample.value = example;
|
|
|
|
App.project.setKey("sceneInfo",example || defaultProjectInfo().sceneInfo);
|
|
}
|
|
|
|
// 确认创建项目
|
|
function handleSubmit() {
|
|
projectInfoFormRef.value.validate().then(() => {
|
|
const submit = async () => {
|
|
const data: ISceneFetchData = projectInfoFormRef.value.getData();
|
|
let cesiumData;
|
|
if (currentType.value === 'WebGIS') {
|
|
cesiumData = cesiumConfigRef.value.getData();
|
|
data.projectType = 1;
|
|
}
|
|
|
|
spin.value = true;
|
|
const res = await fetchAddScene({
|
|
...data,
|
|
id: "",
|
|
zip: "",
|
|
coverPicture:"",
|
|
sceneVersion:1,
|
|
exampleSceneId: data.id,
|
|
cesiumConfig: cesiumData ? JSON.stringify(cesiumData) : undefined
|
|
})
|
|
|
|
spin.value = false;
|
|
|
|
if (res.error === null) {
|
|
window.$dialog.success({
|
|
title: t("other.Tips"),
|
|
content: t("prompt['The project is created successfully. Do you want to enter?']"),
|
|
positiveText: t("other.Ok"),
|
|
negativeText: t('other.Cancel'),
|
|
onPositiveClick: () => {
|
|
// 新窗口打开项目
|
|
window.open(window.location.origin + "/#/editor/" + res.data.id, "_blank");
|
|
|
|
emits('update:visible', false);
|
|
// 刷新父页面列表
|
|
emits('refresh');
|
|
},
|
|
onNegativeClick: () => {
|
|
emits('update:visible', false);
|
|
// 刷新父页面列表
|
|
emits('refresh');
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 如果是创建GIS项目,还得验证GIS表单
|
|
if (currentType.value === 'WebGIS') {
|
|
cesiumConfigRef.value.validate().then(() => {
|
|
submit();
|
|
}).catch(() => {
|
|
settingTabsValue.value = 'cesium'
|
|
})
|
|
} else {
|
|
submit();
|
|
}
|
|
}).catch(() => {
|
|
settingTabsValue.value = 'project'
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
getExampleScene()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<n-modal :show="visible" @update:show="(v) => emits('update:visible',v)">
|
|
<n-spin :show="spin">
|
|
<n-card :title="t('home.New project')" :bordered="false" size="small" class="w-max"
|
|
header-style="background:var(--n-color);">
|
|
<template #header-extra>
|
|
<n-icon size="20" class="cursor-pointer" @click="emits('update:visible',false)">
|
|
<Close/>
|
|
</n-icon>
|
|
</template>
|
|
|
|
<div class="flex flex-wrap justify-between pt-20px h-max">
|
|
<div class="flex flex-col mr-20px">
|
|
<div v-for="type in typeList" :key="type.type" @click="handleChangeProjectType(type)"
|
|
class="relative cursor-pointer">
|
|
<img width="300" :src="type.img" class="mb-15px b-rd-2 transition-all-500"
|
|
:style="{border:currentType === type.type ? '2px solid var(--n-color-target)' : '2px solid transparent'}"/>
|
|
<p class="text-16px absolute bottom-30px left-10px" style="text-shadow: 1px 1px 2px black;">
|
|
{{ type.name }}</p>
|
|
|
|
<EsTip v-if="type.isDev" size="25" class="absolute top-10px right-10px">
|
|
{{ t("prompt['Related modules are under development, unstable versions.']") }}
|
|
</EsTip>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-3 gap-10px grid-auto-rows-max w-560px max-h-full mr-10px">
|
|
<div style="grid-column: 1 / -1;">
|
|
<h4>{{ t("home.Template") }}</h4>
|
|
<n-divider class="!my-10px"/>
|
|
</div>
|
|
|
|
<n-card hoverable @click="handleSelectExample(null)"
|
|
class="w-178px h-max cursor-pointer"
|
|
:style="{border:currentExample === null ? '1px solid var(--n-color-target)' : '1px solid transparent'}"
|
|
:footer-style="`background:var(${currentExample === null ? '--n-color-target' : '--n-action-color'});padding:10px;`">
|
|
<template #cover>
|
|
<img src="/static/images/carousel/Astral3DEditor.jpg" :alt="t('home.Empty project')"
|
|
class="h-110px hover:transform-scale-140 transition-all-200">
|
|
</template>
|
|
|
|
<template #footer>
|
|
<span> {{ t("home.Empty project") }} </span>
|
|
</template>
|
|
</n-card>
|
|
|
|
<n-card hoverable v-for="example in exampleList[currentType]" :key="example.id"
|
|
@click="handleSelectExample(example)"
|
|
class="w-178px h-max cursor-pointer"
|
|
:style="{border:currentExample === example ? '1px solid var(--n-color-target)' : '1px solid transparent'}"
|
|
:footer-style="`background:var(${currentExample === example ? '--n-color-target' : '--n-action-color'});padding:10px;`">
|
|
<template #cover>
|
|
<n-tag type="success" :bordered="false" class="absolute top-10px right-10px z-10">
|
|
{{ example.sceneType }}
|
|
</n-tag>
|
|
<img :src="getServiceStaticFile(example.coverPicture) || '/static/images/carousel/Astral3DEditor.jpg'" :alt="example.sceneName"
|
|
class="h-110px hover:transform-scale-140 transition-all-200">
|
|
</template>
|
|
|
|
<template #footer>
|
|
<span> {{ example.sceneName }} </span>
|
|
</template>
|
|
</n-card>
|
|
</div>
|
|
|
|
<div class="h-full flex">
|
|
<n-divider vertical class="!h-auto"/>
|
|
|
|
<div class="h-full w-320px flex flex-col justify-start ml-10px">
|
|
<img :src="getServiceStaticFile(currentExample?.coverPicture || '') || '/static/images/carousel/Astral3DEditor.jpg'" class="w-full">
|
|
|
|
<h3 class="mt-10px mb-8px">{{ currentExample?.sceneName || t("home.Empty project") }}</h3>
|
|
<p class="h-100px overflow-y-auto">
|
|
{{
|
|
currentExample === null ? t("home.A blank project without any scene elements") : currentExample.sceneIntroduction
|
|
}}</p>
|
|
|
|
<n-button tertiary size="small" class="mt-8px w-full text-left block">
|
|
{{ t("home.Project default settings") }}
|
|
</n-button>
|
|
|
|
<n-tabs v-model:value="settingTabsValue" type="segment" size="small"
|
|
class="project-setting-tabs mt-24px" pane-class="h-220px">
|
|
<n-tab-pane name="project" :tab="t('home.Project')" display-directive="show">
|
|
<ProjectInfoForm :value="currentExample" ref="projectInfoFormRef" class="!p-15px"/>
|
|
</n-tab-pane>
|
|
<n-tab-pane name="cesium" tab="Cesium" v-if="currentType === 'WebGIS'" display-directive="show">
|
|
<CesiumConfig ref="cesiumConfigRef" class="!p-15px"/>
|
|
</n-tab-pane>
|
|
</n-tabs>
|
|
|
|
<div class="w-full text-end mt-15px">
|
|
<n-button type="primary" size="small" @click.stop="handleSubmit">{{ t("other.Ok") }}</n-button>
|
|
<n-button size="small" class="ml-10px" @click="emits('update:visible',false)">{{ t("other.Cancel") }}
|
|
</n-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</n-card>
|
|
</n-spin>
|
|
</n-modal>
|
|
</template>
|
|
|
|
<style scoped lang="less">
|
|
.project-setting-tabs {
|
|
:deep(.n-tabs-nav) {
|
|
justify-content: center;
|
|
|
|
.n-tabs-rail {
|
|
width: 50%;
|
|
}
|
|
}
|
|
}
|
|
</style> |