模型库

This commit is contained in:
2025-09-23 11:25:37 +08:00
parent a9f7d18df4
commit 02d2e92f5c
38 changed files with 2331 additions and 82 deletions

View File

@ -8,26 +8,413 @@
</div>
</div>
</template>
<div class="set_detail"></div>
<div class="set_detail">
<div class="top">
<el-input
v-model="modelName"
class="w-50 m-2"
placeholder="请输入模型名称进行搜索"
:suffix-icon="Search"
/>
<button @click="setting" class="btn">
<svg-icon
name="sys_set"
class="setIcon"
:size="12"
color="rgba(255,255,255, 1)"
style="margin-right: 5px"
></svg-icon
>默认模型参数设置
</button>
</div>
<div class="content">
<!-- 左侧Tab导航 -->
<div class="treeCon">
<el-tree style="max-width: 600px" :data="typeTreeData" ref="treeRef" node-key="id">
<template #default="{ node, data }">
<!-- <span> {{ node.label }}</span> -->
<span
:class="{
'primary-type': !(node.childNodes.length != 0),
'selected-text': node.id === currentTypeId
}"
@click.stop="toggleExpand(node)"
class="allowDrag"
>
<svg-icon
:name="node.expanded ? 'arrow' : 'more'"
:size="12"
color="rgba(0, 255, 255, 1)"
style="margin-right: 5px; margin-left: 5px"
v-if="node.childNodes.length != 0"
@click.stop="toggleExpand(node)"
></svg-icon>
{{ node.label }}</span
>
</template>
</el-tree>
</div>
<div class="model-gallery" ref="galleryRef">
<div class="model-section">
<!-- <h2 class="section-title">{{ categories[Number(currentTypeId)].name }}</h2> -->
<div class="model-grid">
<div
v-for="(model, mIndex) in categories"
:key="mIndex"
class="model-item"
@click="modelClick(mIndex, model)"
>
<div class="imgbg">
<el-image
:src="'http://127.0.0.1:8848' + model.poster"
fit="cover"
class="thumbnail"
>
<template #error>
<div class="image-error">加载失败</div>
</template>
</el-image>
</div>
<div class="model-name" :class="{ isactive: activeIndex == mIndex }">
{{ model.modelName }}
</div>
</div>
</div>
</div>
<div v-show="loading" class="loading-more">加载中...</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { Search } from '@element-plus/icons-vue'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { ModelApi } from '@/api/model/index'
import type { TabsPaneContext } from 'element-plus'
import { useTreeNode } from '../tree/hooks/treeNode'
import { TreeApi } from '@/api/tree'
const { t } = useI18n()
const { findParentId, findTreeIndex, cusAddNodes } = useTreeNode()
const isShowPup = ref(false)
const eventBus: any = inject('bus')
var modelName = ref('')
//tab
interface Model {
id: string
name: string
thumbnail: string
}
interface Category {
id: string
modelName: string
poster: string
data: string
}
// 模拟数据
const categories = ref<Category[]>([])
const activeTab = ref('0')
const galleryRef = ref<HTMLElement>()
const loading = ref(false)
let observer: IntersectionObserver | null = null
// 处理Tab点击
const handleTabClick = (tab: TabsPaneContext) => {
const section = document.querySelector(`.model-section[data-category="${tab.index}"]`)
section?.scrollIntoView({ behavior: 'smooth' })
}
// 初始化交叉观察器
const initObserver = () => {
observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const categoryIndex = parseInt(entry.target.getAttribute('data-category') || '0')
activeTab.value = categoryIndex.toString()
// 加载更多检测
if (categoryIndex === categories.value.length - 1 && entry.intersectionRatio > 0.5) {
loadMoreData()
}
}
})
},
{
threshold: [0, 0.5, 1]
}
)
document.querySelectorAll('.model-section').forEach((section) => {
observer?.observe(section)
})
}
// 模拟加载更多数据
const loadMoreData = () => {
if (loading.value) return
loading.value = true
setTimeout(() => {
categories.value.push({
id: `${categories.value.length + 1}`,
name: `新增分类_${categories.value.length}`,
models: Array(10)
.fill(0)
.map((_, i) => ({
id: `n${i}`,
name: `新增模型_${i}`,
thumbnail: `https://picsum.photos/300/200?random=${i + 100}`
}))
})
loading.value = false
}, 1000)
}
// 滚动时检测可见区域
const handleScroll = () => {
if (!galleryRef.value) return
const scrollPosition = galleryRef.value.scrollTop + 100
const sections = Array.from(document.querySelectorAll('.model-section'))
sections.forEach((section, index) => {
const rect = section.getBoundingClientRect()
if (rect.top <= 450 && rect.bottom >= 450) {
activeTab.value = index.toString()
}
})
}
//-----------tree-----------
const currentTypeId = ref<string>('')
var activeIndex: any = ref(null)
interface TypeNode {
id: string
label: string
parentId: string | null
children?: TypeNode[]
}
const typeTreeData = ref<TypeNode[]>([])
const toggleExpand = (row: any) => {
console.log('opoop')
if (row.childNodes.length != 0) {
// row._expanded = !row._expanded
// 这里需要调用el-table的toggleRowExpansion方法
// 需要通过ref获取table实例
// tableRef.value?.toggleRowExpansion(row, row._expanded)
if (row.expanded) {
row.collapse()
} else {
row.expand()
}
}
getModelListByType(row.data.id)
currentTypeId.value = row.id
// loadModelsByType(row.id)
}
let typeArr = {
point: '点',
line: '线',
area: '面'
}
const modelClick = (index, row) => {
console.log(row, 'dddddd')
activeIndex.value = index
// let selectedNode = window.treeObj.getSelectedNodes()[0]
// if(!selectedNode){
// ElMessage.warning('请选择需要添加的文件夹')
// return
// }
if (!isSetting) {
ElMessage.warning('请先设置模型默认参数')
return
}
isShowPup.value = false
let id = new YJ.Tools().randomString()
let models = new YJ.Obj.BatchModel(
window.earth,
{
id: id,
type: typeArr[isSetting.key],
spacing: isSetting['value'] * 1,
url: 'http://127.0.0.1:8848' + isSetting[row.data]
},
function (data) {
console.log('data,url,source_id', data, row)
// posiArr.forEach((item, index) => {
// let model = new Model(that.sdk, {
// id: 'model' + index,
// show: that.options.show,
// url: that.options.url,
// position: item,
// rotate: that.options.type == '点' ? undefined : { x: 0, y: 0, z: array[1] && (array[1][index] || array[1]) }
// })
// that.pointArr.push(model)
// })
renderModel(data, row)
}
)
}
const renderModel = async (data, model) => {
let selectedNode = window.treeObj.getSelectedNodes()[0]
let z
if (data.positions.length > 0) {
data.positions.forEach(async (position, index) => {
// let source_id = this.$md5(new Date().getTime() + model.model_name+index);
let id = new YJ.Tools().randomString()
if (data.type == '面') {
z = data.rotate.z
} else if (data.type == '线') {
z = data.rotate[index]
}
let option = {
id: id,
position,
name: model.modelName + index,
show: true,
scale: 1,
url: model.data,
maximumScale: 1,
host: 'http://127.0.0.1:8848',
rotate: {
x: 0,
y: 0,
z
}
}
let Model = await new YJ.Obj.Model(window.earth, option)
let DbOption = {
params: option,
id,
sourceName: model.modelName + index,
sourceType: 'model',
parentId: selectedNode
? selectedNode.sourceType == 'directory'
? selectedNode.id
: selectedNode.parentId
: undefined
}
TreeApi.addOtherSource(DbOption)
DbOption.isShow = true
DbOption.params = JSON.stringify(DbOption.params)
cusAddNodes(window.treeObj, DbOption.parentId, [DbOption])
// //鼠标右键点击事件
Model.onRightClick = () => {}
// window._entityMap.set(option.id, Model);
// Model.onClick = () => {
// leftClick(node);
// };
// let detailOption = JSON.parse(JSON.stringify(Model.options));
// detailOption.url = model.model_id + ".glb";
// let node = getNodeData(DbOption, detailOption);
// addSource(node).then((res) => {
// if ([0, 200].includes(res.code)) {
// // cusRenderNode(DbOption) DbOption.p_id
// cusAddNodes(this.treeObj, DbOption.p_id, [node]);
// }
// });
})
}
}
const getModelListByType = (id) => {
let formData = new FormData()
formData.append('modelTypeId', id)
ModelApi.showModelByType(formData).then((res) => {
console.log(res.data, 'cccc')
categories.value = res.data
})
}
const getModelList = async () => {
const res: any = await ModelApi.modelTypeList()
if (res.code == 0 || res.code == 200) {
let data = transformNestedJson(res.data, 'name', 'label')
typeTreeData.value = data
}
}
const transformNestedJson = (data, oldKey, newKey) => {
if (Array.isArray(data)) {
return data.map((item) => transformNestedJson(item, oldKey, newKey))
} else if (data && typeof data === 'object') {
const newObj = {}
for (const key in data) {
// 替换键名
const currentKey = key === oldKey ? newKey : key
// 递归处理子元素
newObj[currentKey] = transformNestedJson(data[key], oldKey, newKey)
}
return newObj
}
return data
}
onMounted(() => {
initObserver()
})
onBeforeUnmount(() => {
observer?.disconnect()
})
//end
eventBus.on('openModel', (data) => {
isShowPup.value = data
if (data) {
getModelList()
getSetting()
}
})
eventBus.on('closeModelSet', (data) => {
isShowPup.value = data
if (data) {
console.log('设置后')
getSetting()
}
})
//查看是否有设置模型设置
var isSetting = null
const getSetting = () => {
ModelApi.getModelSetting().then((res) => {
if (res.code == 0 || res.code == 200) {
isSetting = res.data[res.data.length - 1]
console.log(res, 'resres')
}
})
}
const open = () => {
isShowPup.value = true
}
const close = () => {
isShowPup.value = false
}
const setting = () => {
isShowPup.value = false
eventBus.emit('openModelSetting', (true, isSetting))
}
defineExpose({
open,
@ -73,7 +460,7 @@ defineExpose({
align-items: center;
padding-bottom: 20px;
.system_title {
background: url('../../../../../assets/images/titlebg.png') no-repeat;
background: url('@/assets/images/titlebg.png') no-repeat;
background-size: 100% 100%;
width: 229px;
height: 34px;
@ -200,5 +587,176 @@ defineExpose({
color: #fff;
}
}
.el-input {
width: 300px;
margin-left: 30px;
--el-input-placeholder-color: rgba(173, 241, 255, 1) !important;
--el-input-placeholder-font-size: 14px;
--el-input-text-color: #fff;
--el-input-border-color: rgba(var(--color-sdk-base-rgb), 0.5) !important;
--el-input-hover-border-color: rgba(var(--color-sdk-base-rgb), 0.5) !important;
--el-input-focus-border-color: rgba(var(--color-sdk-base-rgb), 0.5) !important;
}
::v-deep .el-input__wrapper {
background-color: rgba(0, 0, 0, 0.5) !important;
}
.btn {
float: right;
height: 32px;
line-height: 32px;
background: rgba(var(--color-sdk-base-rgb), 0.2) !important;
border: 1px solid rgba(var(--color-sdk-base-rgb), 0.5) !important;
border-radius: 4px;
color: #fff !important;
padding: 0 15px;
}
.btn:hover {
color: rgba(var(--color-sdk-base-rgb), 1) !important;
border: 1px solid rgba(var(--color-sdk-base-rgb), 1) !important;
.setIcon {
color: rgba(var(--color-sdk-base-rgb), 1) !important;
}
}
.content {
margin-top: 20px;
height: 400px;
}
}
</style>
<style lang="scss" scoped>
.model-container {
display: flex;
height: 100vh;
}
.model-tabs {
width: 130px;
height: 100%;
float: left;
}
.treeCon {
width: 140px;
height: 100%;
float: left;
border-right: 1px solid rgba(204, 204, 204, 0.2);
}
.model-gallery {
flex: 1;
/* padding: 20px; */
overflow-y: auto;
height: 100%;
width: calc(100% - 160px);
float: left;
margin-left: 10px;
}
.model-section {
min-height: 10vh;
margin-bottom: 40px;
}
.model-grid {
display: flex;
flex-wrap: wrap;
/* justify-content: space-around; */
gap: 20px;
}
.model-name {
width: 100%;
height: 30px;
line-height: 30px;
text-align: center;
color: rgba(255, 255, 255, 1);
}
.isactive {
color: rgba(var(--color-sdk-base-rgb), 1) !important;
}
.model-item {
border-radius: 8px;
overflow: hidden;
/* box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); */
}
.model-item:hover {
cursor: pointer !important;
color: rgba(var(--color-sdk-base-rgb), 1) !important;
.model-name {
color: rgba(var(--color-sdk-base-rgb), 1) !important;
}
}
.imgbg {
width: 70px;
height: 70px;
background: url('@/assets/images/model-bg.png') no-repeat;
background-size: 100% 100%;
}
.thumbnail {
width: 66px;
height: 66px;
margin-left: 2px;
margin-top: 2px;
}
.loading-more {
text-align: center;
padding: 20px;
}
::v-deep .el-tabs__content {
display: none !important;
}
/* 修改滚动条轨道的颜色 */
::v-deep ::-webkit-scrollbar-track {
background: rgba(var(--color-sdk-base-rgb), 0.1) !important;
}
/* 修改滚动条滑块的样式 */
::v-deep ::-webkit-scrollbar-thumb {
background: rgba(var(--color-sdk-base-rgb), 1) !important;
border-radius: 10px;
}
/* 当滑块被激活(用户点击或拖动时) */
::v-deep ::-webkit-scrollbar-thumb:hover {
background: rgba(var(--color-sdk-base-rgb), 1) !important;
}
</style>
<style>
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
/* 定义背景颜色和圆角 */
::-webkit-scrollbar-thumb {
border-radius: 1em;
background-color: rgba(50, 50, 50, 0.3);
}
/* //定义滚动条轨道 内阴影+圆角 */
::-webkit-scrollbar-track {
border-radius: 1em;
background-color: rgba(50, 50, 50, 0.1);
}
/* tree */
.el-tree-node__content > .el-tree-node__expand-icon {
display: none !important;
}
.el-tree {
background: transparent !important;
--el-tree-node-hover-bg-color: rgba(var(--color-sdk-base-rgb), 0.2) !important;
color: rgba(255, 255, 255, 1) !important;
/* font-size: 12px !important; */
width: 130px;
float: left;
margin-left: 10px;
}
::v-deep .el-text {
color: rgba(255, 255, 255, 1) !important;
font-size: 12px !important;
}
.selected-text {
color: rgba(var(--color-sdk-base-rgb), 1) !important; /* Element UI主色可自定义 */
}
::v-deep .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
border-right: 1px solid rgba(var(--color-sdk-base-rgb), 0.2) !important;
}
</style>