我在写这个Demo的时候遇到了一个Vue组件属性被共用的情况,我想它产生的原因是实例没有创建新的,然后属性就是同一个,这个问题该如何解决呢;
项目路径:https://gitee.com/anxwefndu/web-os-system
效果图片:
相关代码
MyDesktop.vue
<template>
<link rel="stylesheet" href="/style/css/iconfont.css">
<div class="desktop-container">
<div class="single-app" v-for="item in data.appList" :key="item.id">
<img :src="'/style/img/' + item.ico" alt=""/>
<div>{{ item.name }}</div>
</div>
<div class="single-app" v-for="item in data.openAppList" :key="item.id" @dblclick="openApp(item)">
<i class="iconfont" :class="'icon-' + item.ico" :style="{ 'color' : item.color}"></i>
<div>{{ item.name }}</div>
</div>
<div class="single-open-app" v-for="item in data.runningAppList" :key="item.id" @click="topApp($event)" :style="{ 'z-index' : item.zIndex }">
<div class="header" @mousedown="openAppMouseDown(item, $event)" @mouseup="openAppMouseUp(item)">
<i class="iconfont ico" :class="'icon-' + item.ico" :style="{ 'color' : item.color}"></i>
<div class="name">{{ item.name }}</div>
<i class="iconfont icon-close"></i>
<i class="iconfont icon-minsize" v-if="item.status === 'maxsize'"></i>
<i class="iconfont icon-maxsize" v-if="item.status === 'normal'"></i>
<i class="iconfont icon-line"></i>
</div>
<div class="content">
<component :is="item.component"></component>
</div>
</div>
</div>
</template>
<script>
import {defineAsyncComponent, markRaw, onMounted, reactive} from "vue";
export default {
setup: function () {
const data = reactive({
appList: [
{
id: 1,
name: "此电脑",
ico: "computer.png",
},
],
openAppList: [
{
id: 1,
name: "应用市场",
ico: "market",
color: "#1cd67c",
url: ""
},
{
id: 2,
name: "相册",
ico: "img",
color: "#4c35d3",
component: markRaw(defineAsyncComponent(() => import('@/components/MyPhotoAlbum.vue')))
},
{
id: 3,
name: "计算器",
ico: "calculator",
color: "#1d2088",
component: markRaw(defineAsyncComponent(() => import('@/components/MyCalculator.vue')))
},
],
runningAppList: [],
});
let addMousemoveFlag = false;
let isDragging = false;
const minGap = 5;
let windowWidth;
let windowHeight;
let maxZIndex = 1;
onMounted(() => {
const desktopContainer = document.getElementsByClassName("desktop-container")[0];
windowWidth = desktopContainer.clientWidth;
windowHeight = desktopContainer.clientHeight;
});
function openApp(item) {
data.runningAppList.push({
id: data.runningAppList.length + 1,
name: item.name,
ico: item.ico,
color: item.color,
component: item.component,
status: "normal",
zIndex: maxZIndex++,
});
if (!addMousemoveFlag) {
addMousemoveFlag = true;
document.addEventListener('mousemove', (e) => {
if (!isDragging) {
return;
}
for (let i = 0; i < data.runningAppList.length; i++) {
if (data.runningAppList[i].isDragging) {
data.runningAppList[i].dragElem.style.cursor = "move";
const dragElem = data.runningAppList[i].dragElem.parentNode;
const width = data.runningAppList[i].dragElem.parentNode.clientWidth;
const height = data.runningAppList[i].dragElem.parentNode.clientHeight;
if (e.clientX - data.runningAppList[i].initialX - width / 2 < minGap) {
dragElem.style.left = (minGap + width / 2) + 'px';
} else if (e.clientX - data.runningAppList[i].initialX + width / 2 > windowWidth - minGap) {
dragElem.style.left = (windowWidth - minGap - width / 2) + 'px';
} else {
dragElem.style.left = (e.clientX - data.runningAppList[i].initialX) + 'px';
}
if (e.clientY - data.runningAppList[i].initialY - height / 2 < minGap) {
dragElem.style.top = (minGap + height / 2) + 'px';
} else if (e.clientY - data.runningAppList[i].initialY + height / 2 > windowHeight - minGap) {
dragElem.style.top = (windowHeight - minGap - height / 2) + 'px';
} else {
dragElem.style.top = (e.clientY - data.runningAppList[i].initialY) + 'px';
}
}
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
for (let i = 0; i < data.runningAppList.length; i++) {
if (data.runningAppList[i].isDragging) {
data.runningAppList[i].isDragging = false;
data.runningAppList[i].dragElem.style.cursor = "default";
}
}
});
}
}
function openAppMouseDown(item, e) {
const dragElem = e.target.parentNode;
dragElem.style.zIndex = (maxZIndex++) + "";
item.initialX = e.clientX - dragElem.offsetLeft;
item.initialY = e.clientY - dragElem.offsetTop;
item.isDragging = true;
item.dragElem = e.target;
isDragging = true;
}
function openAppMouseUp(item) {
item.isDragging = false;
isDragging = false;
item.dragElem.style.cursor = "default";
}
function topApp(e) {
e.currentTarget.style.zIndex = (maxZIndex++) + "";
}
return {
data,
openApp,
openAppMouseDown,
openAppMouseUp,
topApp,
}
},
};
</script>
<style scoped>
.desktop-container {
width: 100%;
height: calc(100% - 50px);
background-image: url(../../public/style/img/desktop.jpg);
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
position: relative;
}
.single-app {
display: inline-block;
width: 76px;
height: 76px;
position: relative;
user-select: none;
}
.single-app:hover {
background-color: #94b5cd;
cursor: default;
}
.single-app img {
width: 50px;
position: absolute;
top: 4px;
left: 13px;
}
.single-app div {
width: 100%;
padding: 0 4px 0 4px;
text-align: center;
position: absolute;
top: 54px;
left: 0;
font-size: 14px;
color: white;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.single-app i::before {
font-size: 50px;
position: absolute;
top: 4px;
left: 13px;
}
.single-open-app {
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
margin: 0;
padding: 0;
border: 1px solid #b1b1b1;
}
.single-open-app .header {
height: 32px;
background-color: #eff4f9;
font-size: 12px;
line-height: 32px;
border-bottom: 1px solid #b1b1b1;
user-select: none;
cursor: default;
}
.single-open-app .header .ico {
float: left;
width: 32px;
height: 32px;
text-align: center;
}
.single-open-app .header .name {
float: left;
height: 32px;
}
.single-open-app .header .icon-line,
.single-open-app .header .icon-maxsize,
.single-open-app .header .icon-minsize,
.single-open-app .header .icon-close {
float: right;
width: 32px;
height: 32px;
text-align: center;
}
.single-open-app .header .icon-line::before {
color: black;
font-weight: 1000;
}
.single-open-app .header .icon-line:hover,
.single-open-app .header .icon-maxsize:hover,
.single-open-app .header .icon-minsize:hover,
.single-open-app .header .icon-close:hover {
background-color: #dadada;
}
.single-open-app .content {
width: fit-content;
height: fit-content;
}
</style>
MyPhotoAlbum.vue
<template>
<div class="container">
<SlideShow :imgList="data.imgList"></SlideShow>
</div>
</template>
<script>
import SlideShow from "@/components/SlideShow.vue";
// import {createApp, onMounted} from "vue";
export default {
components: {SlideShow},
setup() {
const data = {
imgList: [
"/style/slideShow/1.jpg",
"/style/slideShow/2.jpg",
"/style/slideShow/3.jpg",
"/style/slideShow/4.jpg",
"/style/slideShow/5.jpg",
"/style/slideShow/6.jpg",
"/style/slideShow/7.jpg",
]
}
// onMounted(() => {
// const slideShow = createApp(SlideShow, {
// imgList: data.imgList,
// });
// const container = document.getElementsByClassName("container")[0];
// slideShow.mount(container);
// })
return {
data,
}
}
}
</script>
<style scoped>
.container {
padding: 30px 0;
background-color: #eff4f9;
}
</style>
SlideShow.vue
<template>
<link rel="stylesheet" href="/style/css/iconfont.css">
<div class="img-container">
<i class="iconfont icon-left" @click="changeShowPage(-1)"/>
<img class="img-item" alt="" v-for="(item, index) in props.imgList" :key="index" :src="item" @click="changeShowPage(index - data.curIndex)"/>
<i class="iconfont icon-right" @click="changeShowPage(1)"/>
</div>
</template>
<script>
import {onMounted, reactive} from "vue";
export default {
props: ["imgList"],
setup(props) {
const data = reactive({
curIndex: 0
});
function changeShowPage(operator) {
if (operator < 0) {
if (data.curIndex > 0) {
data.curIndex--;
layout();
}
} else if (operator > 0) {
if (data.curIndex < imgList.length - 1) {
data.curIndex++;
layout();
}
}
}
let imgList;
let zIndexBase = 100;
onMounted(() => {
imgList = document.getElementsByClassName("img-item");
zIndexBase = imgList.length;
data.curIndex = Math.round(imgList.length / 2);
layout();
console.log(data.curIndex);
});
const step = 200;
const scale = 0.7;
function layout() {
for (let i = 0; i < imgList.length; i++) {
imgList[i].style.transform = `translateX(${step * (i - data.curIndex)}px)`;
imgList[i].style.scale = Math.pow(scale, Math.abs(data.curIndex - i));
imgList[i].style.zIndex = zIndexBase - Math.abs(data.curIndex - i);
}
}
return {
data,
changeShowPage,
props,
}
}
};
</script>
<style scoped>
.img-container {
width: 600px;
height: 200px;
position: relative;
user-select: none;
overflow: hidden;
}
.img-item {
height: 200px;
position: absolute;
top: 0;
left: 50%;
margin-left: -100px;
cursor: pointer;
}
.icon-left::before, .icon-right::before {
font-size: 40px;
position: absolute;
top: 50%;
transform: translateY(-50%);
display: inline-block;
width: 50px;
height: 100px;
line-height: 100px;
color: #333232;
background-color: transparent;
z-index: 999;
}
.icon-left::before {
left: 0;
}
.icon-left:hover::before, .icon-right:hover::before {
background-color: #081012aa;
cursor: pointer;
}
.icon-right::before {
transform: rotate(180deg) translateY(50%);
right: 0;
}
</style>
我希望能够不太修改MyDesktop.vue中的大体框架,但如果有什么更好的设计思路,也可以提出来,我可以进行修改