bingbingyihao 2024-02-05 09:01 采纳率: 100%
浏览 14
已结题

关于Vue组件属性被共用情况的解决办法

我在写这个Demo的时候遇到了一个Vue组件属性被共用的情况,我想它产生的原因是实例没有创建新的,然后属性就是同一个,这个问题该如何解决呢;
项目路径:https://gitee.com/anxwefndu/web-os-system

效果图片:

img

相关代码

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中的大体框架,但如果有什么更好的设计思路,也可以提出来,我可以进行修改

  • 写回答

9条回答 默认 最新

  • 百锦再@新空间代码工作室 全栈领域优质创作者 2024-02-05 09:27
    关注

    作为也用过vue的我,有三个方案

    第一个方案:就是一个弹框 创建一个实例。你明白我的意思吧,你都找到原因了,是因为公用实例,那就不要共用啊。
    第二个方案:就是复制组件代码,另起一个名字,然后创建的时候,各用个,但是这种办法适合,组件复用不是很频繁的情况,否则,要创建一堆的冗余代码;
    第三个方案,我比较倾向于这个:同一个属性被共用,没关系。如果是两种使用的话,那么两种使用的时候给传值打一个标记,然后就可以根据传值不同,进行判断和属性赋值处理了。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(8条)

报告相同问题?

问题事件

  • 系统已结题 2月13日
  • 已采纳回答 2月5日
  • 创建了问题 2月5日

悬赏问题

  • ¥15 关于#计算机视觉#的问题:求一份高质量桥梁多病害数据集
  • ¥50 如何将脑的图像投影到颅骨上
  • ¥15 提问一个关于vscode相关的环境配置问题,就是输入中文但是显示不出来,代码在idea可以显示中文,但在vscode不行,不知道怎么配置环境
  • ¥15 netcore使用PuppeteerSharp截图
  • ¥20 这张图页脚具体代码该怎么写?
  • ¥20 WPF MVVM模式 handycontrol 框架, hc:SearchBar 控件 Text="{Binding NavMenusKeyWords}" 绑定取不到值
  • ¥15 需要手写数字信号处理Dsp三个简单题 不用太复杂
  • ¥15 数字信号处理考试111
  • ¥15 allegro17.2生成bom表是空白的
  • ¥15 请问一下怎么打通CAN通讯