接入3d 创建画布相关接口
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
let data = reactive({
|
||||
})
|
||||
onMounted(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="modalDetail">
|
||||
<div class="title">
|
||||
Properties Information
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="name">
|
||||
<div class="title fs18">
|
||||
Sofa
|
||||
</div>
|
||||
<div class="fs14 c66">
|
||||
Model Name
|
||||
</div>
|
||||
</div>
|
||||
<div class="fs14 c18">
|
||||
Transform
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div>
|
||||
<div class="fs14 c18">X</div>
|
||||
<div class="fs12 c66">1.0</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Y</div>
|
||||
<div class="fs12 c66">1.0</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Z</div>
|
||||
<div class="fs12 c66">1.0</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fs14 c18">
|
||||
Dimensions
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div>
|
||||
<div class="fs14 c18">Height</div>
|
||||
<div class="fs12 c66">22</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Width</div>
|
||||
<div class="fs12 c66">22</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Depth</div>
|
||||
<div class="fs12 c66">22</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.modalDetail{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
> .title{
|
||||
margin: 2.4rem 0;
|
||||
font-weight: 500;
|
||||
font-size: 2rem;
|
||||
line-height: 2.7rem;
|
||||
color: #000;
|
||||
}
|
||||
> .detail{
|
||||
> .name{
|
||||
> .title{
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
}
|
||||
> .flex{
|
||||
display: flex;
|
||||
> div{
|
||||
margin-right: 4.8rem;
|
||||
:last-child{
|
||||
margin-top: .2rem;
|
||||
}
|
||||
&:last-child{
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
> div{
|
||||
font-weight: 500;
|
||||
margin-bottom: 2.4rem;
|
||||
&:last-child{
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
}
|
||||
.fs18{
|
||||
font-size: 1.8rem;
|
||||
color: #000;
|
||||
}
|
||||
.fs12{
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.fs14{
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.c18{
|
||||
color: #181818;
|
||||
}
|
||||
.c66{
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
|
||||
import model from './model.vue'
|
||||
import detail from './detail.vue'
|
||||
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
let data = reactive({
|
||||
})
|
||||
const modelRef = ref(null)
|
||||
onMounted(()=>{
|
||||
modelRef.value.open()
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="three-model">
|
||||
<div class="modelBox">
|
||||
<model ref="modelRef" />
|
||||
</div>
|
||||
<div class="detailBox">
|
||||
<detail ref="detailRef" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.three-model{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
padding: 6.7rem 13rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
> .modelBox{
|
||||
height: 100%;
|
||||
width: 65.5rem;
|
||||
}
|
||||
> .detailBox{
|
||||
width: 22rem;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,188 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, shallowRef, nextTick } from "vue";
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import gsap from 'gsap';
|
||||
import * as THREE from 'three';
|
||||
import { initThree,clearModel,addModel } from './threeTool'
|
||||
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
|
||||
const threeDom = ref()//threeDom元素
|
||||
|
||||
let scene = shallowRef()//场景
|
||||
let group = shallowRef()//组
|
||||
let camera = shallowRef()//相机
|
||||
let renderer = shallowRef()//渲染器
|
||||
let pointLight = shallowRef();//光
|
||||
let ambient = shallowRef()//环境光
|
||||
let controls = shallowRef()//监听鼠标、键盘事件
|
||||
|
||||
const animationId = ref(null);
|
||||
//加载进度
|
||||
const load = ref({
|
||||
state:false,
|
||||
progress:0
|
||||
})
|
||||
// const textureLoader = ref(new THREE.TextureLoader())//材质
|
||||
//初始化
|
||||
const init = ()=>{
|
||||
//初始化threejs
|
||||
if(scene.value)return
|
||||
const initResult = initThree(threeDom.value)
|
||||
scene.value = initResult.scene
|
||||
group.value = initResult.group
|
||||
camera.value = initResult.camera
|
||||
renderer.value = initResult.renderer
|
||||
ambient.value = initResult.ambient
|
||||
controls.value = initResult.controls
|
||||
pointLight.value = initResult.pointLight
|
||||
|
||||
threeDom.value.ondblclick = (event:any)=>{
|
||||
let intersects = openModel(event);
|
||||
if(!intersects || intersects.length<=0) return
|
||||
const bbox = new THREE.Box3().setFromObject(intersects[0].object);
|
||||
const size = new THREE.Vector3();
|
||||
let target2 = bbox.getCenter(size);//获取选中包围起来后的中心坐标
|
||||
animateCamera(camera.value.position,intersects[0].point,controls.value.target,target2)
|
||||
}
|
||||
}
|
||||
|
||||
let openModel = (event:any)=>{
|
||||
let mouse = new THREE.Vector2();
|
||||
let raycaster = new THREE.Raycaster();
|
||||
mouse.x=(event.clientX/window.innerWidth)*2-1;
|
||||
mouse.y=-(event.clientY/window.innerHeight)*2+1;
|
||||
raycaster.setFromCamera(mouse, camera.value);
|
||||
let intersects = raycaster.intersectObjects(scene.value.children);
|
||||
return intersects
|
||||
}
|
||||
let isTweening = false;
|
||||
function animateCamera(current1:any, target1:any, current2:any, target2:any){
|
||||
if (isTweening) return
|
||||
isTweening = true
|
||||
let options = {
|
||||
x1: current1.x, // 相机当前位置x
|
||||
y1: current1.y, // 相机当前位置y
|
||||
z1: current1.z, // 相机当前位置z
|
||||
x2: current2.x, // 控制当前的中心点x
|
||||
y2: current2.y, // 控制当前的中心点y
|
||||
// z2: current2.z // 控制当前的中心点z
|
||||
}
|
||||
gsap.to(options,{
|
||||
x1: 0, // 新的相机位置x
|
||||
y1: target2.y, // 新的相机位置y
|
||||
z1: 1000, // 新的相机位置z
|
||||
x2: 0, // 新的控制中心点位置x
|
||||
y2: target2.y, // 新的控制中心点位置x
|
||||
duration:1,
|
||||
ease:'linear',
|
||||
onUpdate:()=>{
|
||||
camera.value.position.x = options.x1;
|
||||
camera.value.position.y = options.y1;
|
||||
camera.value.position.z = options.z1;
|
||||
controls.value.target.x = options.x2;
|
||||
controls.value.target.y = options.y2;
|
||||
// controls.value.target.z = object.z2;
|
||||
controls.value.update();
|
||||
},
|
||||
onComplete:()=>{
|
||||
isTweening = false
|
||||
}
|
||||
// z2: target2.z // 新的控制中心点位置x
|
||||
})
|
||||
}
|
||||
const setModel = async (url:any)=>{
|
||||
clearModel(group,scene)
|
||||
await addModel(url,controls,camera,pointLight,group,load)
|
||||
// addMaterial()
|
||||
}
|
||||
const open = async ()=>{
|
||||
load.value.state = true
|
||||
await nextTick(()=>{
|
||||
init()
|
||||
})
|
||||
controls.value.enableDamping = true;
|
||||
let animate = ()=>{
|
||||
animationId.value = requestAnimationFrame(animate);
|
||||
// renderer.value.render(scene.value, camera.value);
|
||||
// model.rotation.x += 0.01; //旋转物体
|
||||
var vector = camera.value.position.clone()
|
||||
controls.value.update();
|
||||
renderer.value.render(scene.value, camera.value);
|
||||
// point.position.set(vector.x,vector.y,vector.z);
|
||||
// group.rotation.y += 0.01;
|
||||
// composer.render();
|
||||
};
|
||||
animate();
|
||||
await setModel("https://www.minio-api.aida.com.hk/aida-threed/female/glb/1%E7%9F%AD%E8%A2%96T%E6%81%A4.glb?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260310%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260310T032933Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=184ec7f9ff3076dde5aca66e2d2e27f8d180add698a5b1040fe903a55cb2f85e")
|
||||
load.value.state = false
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({open})
|
||||
</script>
|
||||
<template>
|
||||
<div class="modelBox">
|
||||
<div class="model" ref="threeDom">
|
||||
</div>
|
||||
<div class="load" v-show="load.state">
|
||||
<i class="fi fi-rr-cubes"></i>
|
||||
<div class="text">Load...</div>
|
||||
<div class="loadBox">
|
||||
<div class="schedule" :style="{width:load.progress+'%'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.modelBox{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
>.model{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid #D9D9D9;
|
||||
overflow: hidden;
|
||||
}
|
||||
> .load{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, .2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
color: #fff;
|
||||
> i{
|
||||
font-size: 3rem;
|
||||
}
|
||||
> .loadBox{
|
||||
width: 15rem;
|
||||
height: 1rem;
|
||||
border-radius: 1rem;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
> .schedule{
|
||||
height: 100%;
|
||||
background: greenyellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,125 @@
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
|
||||
|
||||
export const initThree = (threeDom)=>{
|
||||
const scene = new THREE.Scene();
|
||||
const group = new THREE.Group()
|
||||
scene.add(group)
|
||||
|
||||
//创建相机对象
|
||||
// this.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
|
||||
const camera = new THREE.PerspectiveCamera(45, threeDom.offsetWidth / threeDom.offsetHeight, 0.1, 10000);
|
||||
camera.position.set(0, 90, 6); //设置相机位置
|
||||
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
|
||||
/**
|
||||
* 创建渲染器对象
|
||||
*/
|
||||
let width = threeDom.offsetWidth; //窗口宽度
|
||||
let height = threeDom.offsetHeight; //窗口高度
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
logarithmicDepthBuffer: true,//深度缓存 防止模型闪烁重影
|
||||
});
|
||||
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;//设置色调
|
||||
renderer.toneMappingExposure = 1.3;
|
||||
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(width, height); //设置渲染区域尺寸
|
||||
renderer.setClearColor(0xffffff, 1); //设置背景颜色
|
||||
threeDom.innerHTML = '';
|
||||
threeDom.appendChild(renderer.domElement);
|
||||
|
||||
// 设置渲染器大小
|
||||
//环境光
|
||||
let ambient = new THREE.AmbientLight(0xffffff,.8);
|
||||
scene.add(ambient);
|
||||
let controls = new OrbitControls(camera,renderer.domElement)//监听鼠标、键盘事件;
|
||||
// controls.minDistance = 500; // 设置相机与焦点的最小距离
|
||||
// controls.maxDistance = 4000; // 设置相机与焦点的最大距离
|
||||
controls.mouseButtons = {
|
||||
// LEFT:THREE.MOUSE.PAN, // 左键 拖动(默认旋转:ROTATE)
|
||||
LEFT:THREE.MOUSE.ROTATE, // 左键 拖动(默认旋转:ROTATE)
|
||||
MIDDLE:THREE.MOUSE.DOLLY, // 滑轮 缩放
|
||||
RIGHT:THREE.MOUSE.PAN // 右键 旋转(默认拖动:PAN)
|
||||
// RIGHT:THREE.MOUSE.ROTAafTE // 右键 旋转(默认拖动:PAN)
|
||||
}
|
||||
/**
|
||||
* 光源设置
|
||||
*/
|
||||
//点光源
|
||||
/**
|
||||
* AmbientLight 环境光
|
||||
PointLight 点光源
|
||||
DirectionalLight 平行光,比如太阳光
|
||||
SpotLight 聚光源
|
||||
*/
|
||||
const pointLight = new THREE.DirectionalLight(0xffffff,.5);
|
||||
pointLight.intensity = 1.2
|
||||
pointLight.castShadow = true//开启阴影
|
||||
pointLight.shadow.mapSize = new THREE.Vector2(width, height)
|
||||
scene.add(pointLight); //点光源添加到场景中
|
||||
// pointLight.position.set(400, 200, 300); //点光源位置
|
||||
pointLight.position.y = 400;
|
||||
pointLight.position.z = 200;
|
||||
pointLight.position.x = 200;
|
||||
let floorGeometry = new THREE.PlaneGeometry(5000, 3000)//地板大小
|
||||
let floorMaterial = new THREE.MeshPhongMaterial({ color: "#7e7ab0", })
|
||||
let floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
|
||||
floorMesh.rotation.x = -0.5 * Math.PI;
|
||||
floorMesh.receiveShadow = true;
|
||||
floorMesh.position.y = -0.001;
|
||||
// scene.add(floorMesh);
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
// const texture = textureLoader.load('/3dModel/sketch-thick.jpg');
|
||||
scene.background = new THREE.Color("#fff");
|
||||
return {scene,group,camera,renderer,ambient,controls,pointLight}
|
||||
}
|
||||
export const clearModel = (group,scene)=>{
|
||||
const oldGroup:any = group.value;
|
||||
group.value = new THREE.Group();
|
||||
scene.value.add(group.value);
|
||||
scene.value.remove(oldGroup);
|
||||
}
|
||||
|
||||
|
||||
export const addModel = async (
|
||||
url:any,
|
||||
controls:OrbitControls,
|
||||
camera:THREE.PerspectiveCamera,
|
||||
pointLight:THREE.DirectionalLight,
|
||||
group:THREE.Group,
|
||||
load:any)=>{
|
||||
await new Promise((resolve, reject) => {
|
||||
var fbxLoader = new GLTFLoader();
|
||||
let drac = new DRACOLoader()
|
||||
drac.setDecoderPath('/draco/')
|
||||
fbxLoader.setDRACOLoader(drac)
|
||||
// fbxLoader.load('/3dModel/222/1111.glb',
|
||||
fbxLoader.load(url,
|
||||
|
||||
(obj:any) => {
|
||||
let scene = obj.scene;
|
||||
var box = new THREE.Box3().setFromObject(scene);
|
||||
var center = box.getCenter(new THREE.Vector3());
|
||||
controls.value.target.copy(center);
|
||||
// controls.autoRotate = true
|
||||
camera.value.position.y = center.y;
|
||||
camera.value.position.z = 1000;
|
||||
pointLight.value.position.y = 250;
|
||||
pointLight.value.position.z = 1250;
|
||||
group.value.add(scene);
|
||||
resolve('')
|
||||
},(xhr:any) => { // 加载进度回调
|
||||
const percent = xhr.total == 0?100:(xhr.loaded / xhr.total * 100).toFixed(2);
|
||||
load.value.progress = percent
|
||||
// updateProgressBar(Number(percent));
|
||||
},(error:any) => { // 加载失败回调
|
||||
console.error('模型加载失败:', error);
|
||||
reject('')
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -53,8 +53,8 @@
|
||||
@home="() => fitView({ maxZoom: 1 })"
|
||||
/>
|
||||
<image-preview ref="imagePreviewRef" />
|
||||
<baseModal ref="three">
|
||||
|
||||
<baseModal ref="threeModelRef" >
|
||||
<threeModel />
|
||||
</baseModal>
|
||||
</template>
|
||||
|
||||
@@ -68,11 +68,16 @@
|
||||
import zoom from '../components/zoom.vue'
|
||||
import imagePreview from '../components/image-preview.vue'
|
||||
import baseModal from '../components/base-modal.vue'
|
||||
// 工具
|
||||
import threeModel from './components/tools/threeModel/index.vue'
|
||||
// 节点
|
||||
import node from './components/node.vue'
|
||||
import resultImage from './components/nodes/result-image.vue'
|
||||
import card from './components/nodes/cards/index.vue'
|
||||
import text from './components/nodes/text.vue'
|
||||
|
||||
// 接口
|
||||
import { getSketchFlowCanvas, putSketchFlowCanvas } from '@/api/flow-canvas'
|
||||
const components = {
|
||||
[NODE_COMPONENT.RESULT_IMAGE]: resultImage,
|
||||
[NODE_COMPONENT.CARD]: card,
|
||||
@@ -166,16 +171,25 @@
|
||||
// 导出流程
|
||||
const exportFlow = () => {
|
||||
// flowManager.exportFlow()
|
||||
|
||||
const str = JSON.stringify(stateManager.nodes.value)
|
||||
const json = JSON.parse(str)
|
||||
localStorage.setItem('flow_json', str)
|
||||
putSketchFlowCanvas({
|
||||
id: props.config.id || '==========',
|
||||
canvasData: str
|
||||
}).then((res) => {
|
||||
if (res) {
|
||||
console.log(res)
|
||||
}
|
||||
})
|
||||
// localStorage.setItem('flow_json', str)
|
||||
}
|
||||
// 导入流程
|
||||
const importFlow = async () => {
|
||||
const importFlow = async (json) => {
|
||||
try {
|
||||
stateManager.nodes.value = []
|
||||
await nextTick()
|
||||
const json = JSON.parse(localStorage.getItem('flow_json') || '[]')
|
||||
// const json = JSON.parse(localStorage.getItem('flow_json') || '[]')
|
||||
stateManager.nodes.value = json
|
||||
setTimeout(() => {
|
||||
nextTick(() => {
|
||||
@@ -188,24 +202,42 @@
|
||||
}
|
||||
|
||||
const imagePreviewRef = ref<any>()
|
||||
const threeModelRef = ref<any>()
|
||||
/** 打开图片预览 */
|
||||
const openImagePreview = (url: string) => {
|
||||
imagePreviewRef.value.open(url)
|
||||
}
|
||||
provide('openImagePreview', openImagePreview)
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
// window['vueFlow'] = vueFlow
|
||||
// window['nodes'] = nodes
|
||||
nodeManager.createResultNode({
|
||||
data: {
|
||||
disableDelete: true,
|
||||
isHeader: false,
|
||||
data: {
|
||||
url: props.config.url
|
||||
let json = []
|
||||
await new Promise((resolve) => {
|
||||
getSketchFlowCanvas({ id: props.config.id || '==========' }).then((res) => {
|
||||
if (res) {
|
||||
console.log(res)
|
||||
json = res.data
|
||||
}
|
||||
}
|
||||
resolve(true)
|
||||
}).catch(() => {
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
if(json.length > 0){
|
||||
importFlow(json)
|
||||
}else{
|
||||
nodeManager.createResultNode({
|
||||
data: {
|
||||
disableDelete: true,
|
||||
isHeader: false,
|
||||
data: {
|
||||
url: props.config.url
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
onBeforeMount(() => {
|
||||
eventManager.removeEvents() // 移除事件
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
v-model="showDialog"
|
||||
align-center
|
||||
:show-close="false"
|
||||
width="70vw"
|
||||
style="border-radius: 20px; padding: 0; --el-dialog-padding-primary: 0"
|
||||
:width="modalWidth"
|
||||
style="border-radius: 2.9rem; padding: 0; --el-dialog-padding-primary: 0;background-color: #f7f7f7;"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<div class="header-close" @click="close">
|
||||
@@ -20,7 +20,13 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, onBeforeUnmount, shallowRef } from 'vue'
|
||||
const showDialog = ref(false)
|
||||
const props = defineProps({
|
||||
modalWidth: {
|
||||
type: String,
|
||||
default: '63vw'
|
||||
}
|
||||
})
|
||||
const showDialog = ref(true)
|
||||
const open = (url_: any) => {
|
||||
showDialog.value = true
|
||||
}
|
||||
@@ -37,15 +43,15 @@
|
||||
<style lang="less" scoped>
|
||||
.header-close {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 40px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
top: 3rem;
|
||||
right: 4rem;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
cursor: pointer;
|
||||
z-index: 1000;
|
||||
}
|
||||
.modal-box {
|
||||
width: 100%;
|
||||
height: 70vh;
|
||||
padding: 67px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user