lsc 2 years ago
parent
commit
58ce84b39f

+ 54 - 0
package-lock.json

@@ -1843,6 +1843,16 @@
       "integrity": "sha1-ACwZkJEtDVlYDJO9NsBW3pnkJZo=",
       "dev": true
     },
+    "clipboard": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.10.tgz",
+      "integrity": "sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==",
+      "requires": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
     "cliui": {
       "version": "2.1.0",
       "resolved": "https://registry.npm.taobao.org/cliui/download/cliui-2.1.0.tgz?cache=0&sync_timestamp=1604880033053&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-2.1.0.tgz",
@@ -3344,6 +3354,11 @@
         }
       }
     },
+    "delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
     "depd": {
       "version": "1.1.2",
       "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz",
@@ -4467,6 +4482,14 @@
         "slash": "^1.0.0"
       }
     },
+    "good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+      "requires": {
+        "delegate": "^3.1.2"
+      }
+    },
     "graceful-fs": {
       "version": "4.2.6",
       "resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.6.tgz",
@@ -9690,6 +9713,11 @@
         "raw-loader": "~0.5.1"
       }
     },
+    "select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+    },
     "select-hose": {
       "version": "2.0.0",
       "resolved": "https://registry.npm.taobao.org/select-hose/download/select-hose-2.0.0.tgz",
@@ -10556,6 +10584,11 @@
       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
       "dev": true
     },
+    "tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
     "to-arraybuffer": {
       "version": "1.0.1",
       "resolved": "https://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz",
@@ -11003,6 +11036,22 @@
       "integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=",
       "dev": true
     },
+    "v-viewer": {
+      "version": "1.6.4",
+      "resolved": "https://registry.npmmirror.com/v-viewer/-/v-viewer-1.6.4.tgz",
+      "integrity": "sha512-LVkiUHpmsbsZXebeNXnu8krRCi5i2n07FeLFxoIVGhw8lVvTBO0ffpbDC6mLEuacCjrIh09HjIqpciwUtWE8lQ==",
+      "requires": {
+        "throttle-debounce": "^2.0.1",
+        "viewerjs": "^1.5.0"
+      },
+      "dependencies": {
+        "throttle-debounce": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz",
+          "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ=="
+        }
+      }
+    },
     "validate-npm-package-license": {
       "version": "3.0.4",
       "resolved": "https://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz",
@@ -11130,6 +11179,11 @@
         "global": "^4.3.1"
       }
     },
+    "viewerjs": {
+      "version": "1.10.5",
+      "resolved": "https://registry.npmmirror.com/viewerjs/-/viewerjs-1.10.5.tgz",
+      "integrity": "sha512-QwKrmXlSfKg5x4y74F/jicpHIRqBMMfHXyboOxHDi5n4XAaejjpalphPq4/HW6venQAoMiD57HpVwBk0JvqpSA=="
+    },
     "vm-browserify": {
       "version": "1.1.2",
       "resolved": "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz?cache=0&sync_timestamp=1572870717730&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvm-browserify%2Fdownload%2Fvm-browserify-1.1.2.tgz",

+ 2 - 0
package.json

@@ -11,10 +11,12 @@
   },
   "dependencies": {
     "axios": "^0.21.1",
+    "clipboard": "^2.0.10",
     "element-ui": "^2.15.1",
     "file-saver": "^2.0.5",
     "jquery": "^3.6.0",
     "qs": "^6.10.1",
+    "v-viewer": "^1.6.4",
     "vant": "^2.12.10",
     "vue": "^2.5.2",
     "vue-cookies": "^1.7.4",

+ 4 - 1
src/App.vue

@@ -23,7 +23,7 @@
         </div>
       </div> -->
 		<!-- </div> -->
-		<div>
+		<div :class="{gHeight: $route.path == '/Grid'}">
 			<!-- main 内容 -->
 			<keep-alive v-if="$route.meta.keepAlive">
 				<!-- 这里是会被缓存的视图组件 -->
@@ -310,4 +310,7 @@
 	.stuWidth {
 		min-width: 1180px;
 	}
+	.gHeight{
+		height: 100%;
+	}
 </style>

BIN
src/assets/grid/add.png


BIN
src/assets/grid/coin1.png


BIN
src/assets/grid/coin2.png


BIN
src/assets/grid/coin3.png


BIN
src/assets/grid/coin4.png


BIN
src/assets/grid/delete.png


BIN
src/assets/grid/deleteT.png


BIN
src/assets/grid/edit.png


BIN
src/assets/grid/image.png


BIN
src/assets/grid/kuan.png


BIN
src/assets/grid/list.png


BIN
src/assets/grid/portal.png


BIN
src/assets/grid/upload.png


BIN
src/assets/grid/video.png


BIN
src/assets/grid/videoH.png


BIN
src/assets/grid/zan.png


BIN
src/assets/loading.gif


+ 1197 - 0
src/components/pages/Grid.vue

@@ -0,0 +1,1197 @@
+<template>
+  <div class="pb_content g_body">
+    <div>
+      <el-image class="coin coin1" :src="require('../../assets/grid/coin1.png')" fit="cover"></el-image>
+      <el-image class="coin coin2" :src="require('../../assets/grid/coin2.png')" fit="cover"></el-image>
+      <el-image class="coin coin3" :src="require('../../assets/grid/coin3.png')" fit="cover"></el-image>
+      <el-image class="coin coin4" :src="require('../../assets/grid/coin4.png')" fit="cover"></el-image>
+    </div>
+    <div class="grid_member">
+      <div class="member_imgbox">
+        <el-image
+          v-for="item in 4"
+          :key="item"
+          class="member_img"
+          :src="require('../../assets/grid/portal.png')"
+          fit="cover"
+        ></el-image>
+      </div>
+      <el-button type="text" @click="dialogVisible2 = true">邀请成员</el-button>
+      <el-button type="text">成员</el-button>
+      <el-button type="text" v-if="RoomInfo.userid == userid">解散房间</el-button>
+    </div>
+    <div
+      v-for="(item,index) in data"
+      :key="index"
+      class="g_box"
+      @mouseover="pIndex = index"
+      @mouseleave="pIndex = ''"
+    >
+      <div
+        :class="{'left-popover':index<3,'right-popover':index==3,'top-popover':index>3,'visibleO':pIndex === index && item && (item.video || item.photo)}"
+      >
+        <div class="pChild" @click="check(index)">
+          <el-image :src="require('../../assets/grid/upload.png')" fit="cover"></el-image>
+          <span>修改</span>
+        </div>
+        <div class="pChild" @click="deleteGrid(index)">
+          <el-image :src="require('../../assets/grid/deleteT.png')" fit="cover"></el-image>
+          <span>删除</span>
+        </div>
+        <div class="pChild" @click="check(index,2)">
+          <el-image :src="require('../../assets/grid/edit.png')" fit="cover"></el-image>
+          <span>添加备注</span>
+        </div>
+      </div>
+      <div
+        v-if="item && item.remarks"
+        :class="{'remark-right-popover':index>12 && index!=15,'remark-left-popover':index==15,'remark-bottom-popover':index<12,'visibleO':pIndex === index && item && item.remarks}"
+      >
+        <div class="Rbox" v-for="(x,y) in item.remarks" :key="y">
+          <div v-if="x.remarks!=''" style="display:flex">
+            <el-image class="Rportal" :src="require('../../assets/grid/portal.png')" fit="cover"></el-image>
+            <div class="RContent">
+              <div>{{x.username}}</div>
+              <div>{{x.remarks}}</div>
+            </div>
+          </div>
+          <div v-else-if="x.remarks==''&& y==0">{{x.username}}暂无备注</div>
+        </div>
+      </div>
+      <el-image v-if="item && item.photo" v-viewer class="photo_img" :src="item.photo" fit="cover"></el-image>
+      <video-player
+        v-else-if="item && item.video"
+        class="video-player vjs-custom-skin"
+        ref="videoPlayer"
+        :playsinline="true"
+        :options="item.video"
+        style="width: 85%;"
+      ></video-player>
+      <el-image
+        v-else
+        class="add_img"
+        :src="require('../../assets/grid/add.png')"
+        fit="cover"
+        @click="check(index)"
+      ></el-image>
+    </div>
+    <el-dialog
+      title="上传文件"
+      :visible.sync="dialogVisible"
+      :append-to-body="true"
+      width="800px"
+      :before-close="handleClose"
+      class="look_notice"
+    >
+      <div slot="title" class="header-title">
+        <div class="title_add_student">上传文件</div>
+      </div>
+      <div>
+        <div class="upload_box" v-show="!file.userid || file.userid == userid">
+          <el-image class="list_img" :src="require('../../assets/grid/list.png')" fit="cover"></el-image>
+          <div class="upload_content">
+            <div
+              @click="addEvent($event)"
+              @mouseover="file.photo ? deleteVisible1 = true:''"
+              @mouseleave="file.photo ? deleteVisible1 = false:''"
+            >
+              <el-image
+                class="delete_img"
+                :src="require('../../assets/grid/delete.png')"
+                fit="cover"
+                v-if="deleteVisible1"
+                @click.stop="deleteFile(1)"
+              ></el-image>
+              <el-image
+                class="upload_img"
+                :src="file.photo ? file.photo : require('../../assets/grid/image.png')"
+                fit="cover"
+              ></el-image>
+              <input
+                type="file"
+                accept="image/*"
+                style="display: none"
+                ref="pathClear1"
+                @change="beforeUpload($event, 1)"
+              />
+            </div>
+            <div
+              style="margin-left: 20px;"
+              @click="addEvent($event)"
+              @mouseover="file.video ? deleteVisible2 = true:''"
+              @mouseleave="file.video ? deleteVisible2 = false:''"
+            >
+              <el-image
+                class="delete_img"
+                :src="require('../../assets/grid/delete.png')"
+                fit="cover"
+                v-if="deleteVisible2"
+                @click.stop="deleteFile(2)"
+              ></el-image>
+              <el-image
+                class="upload_img"
+                :src="file.video ? require('../../assets/grid/videoH.png') : require('../../assets/grid/video.png')"
+                fit="cover"
+              ></el-image>
+              <input
+                type="file"
+                accept="video/mp4, video/quicktime, video/x-msvideo"
+                style="display: none"
+                ref="pathClear2"
+                @change="beforeUpload($event, 2)"
+              />
+            </div>
+          </div>
+        </div>
+        <div class="textarea_box">
+          <div class="textarea_title">添加描述</div>
+          <el-input
+            type="textarea"
+            v-model="remarks"
+            placeholder="请输入描述..."
+            class="textarea_content"
+            :rows="5"
+            resize="none"
+          ></el-input>
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button class="close" @click="addGrid()" type="primary">确定</el-button>
+      </span>
+    </el-dialog>
+    <el-dialog
+      title="邀请成员"
+      :visible.sync="dialogVisible2"
+      :append-to-body="true"
+      width="400px"
+      :before-close="handleClose"
+      class="look_notice invite_dialog"
+    >
+      <div slot="title" class="header-title">
+        <div class="title_add_student">邀请成员</div>
+      </div>
+      <div>
+        <div class="invite_box">
+          <div class="invite_title" ref="inviteT">{{userinfo.name}}邀请您参加思维网格</div>
+          <div class="line"></div>
+          <div class="invite_link">
+            <div ref="invite1">复制链接加入房间:</div>
+            <div ref="invite2">https://baidu.com</div>
+          </div>
+          <div class="invite_num" ref="invite3">
+            房间号:
+            <span>{{RoomInfo.num}}</span>
+          </div>
+          <div class="invite_num" ref="invite4">复制房间号信息,打开思维网格输入即可加入。</div>
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button
+          class="close tag-read"
+          @click="copy"
+          type="primary"
+          :data-clipboard-text="copyText"
+        >一键复制信息</el-button>
+      </span>
+    </el-dialog>
+    <el-dialog
+      title="创建/加入房间"
+      :visible.sync="dialogVisible3"
+      :append-to-body="true"
+      width="400px"
+      :before-close="handleClose2"
+      class="look_notice invite_dialog room_dialog"
+    >
+      <div slot="title" class="header-title">
+        <div class="title_add_student">创建/加入房间</div>
+      </div>
+      <div class="addRoom_box">
+        <div>
+          <el-input v-model="goNum" placeholder="请输入要加入的房间号"></el-input>
+          <div class="room_b">备注:输入房间号点击加入房间或点击创建房间直接创建房间</div>
+          <div class="room_btn">
+            <el-button class="roomBtn" type="primary" @click="goRoom(goNum)">加入房间</el-button>
+            <el-button class="roomBtn" type="primary" @click="checkNum">创建房间</el-button>
+          </div>
+        </div>
+      </div>
+    </el-dialog>
+    <div v-if="proVisible" class="mask">
+      <div class="progressBox">
+        <div class="lbox">
+          <img :src="require('../../assets/loading.gif')" />上传中,请稍后
+        </div>
+        <el-progress
+          :text-inside="true"
+          :stroke-width="20"
+          :percentage="progress"
+          style="width:80%"
+        ></el-progress>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Clipboard from "clipboard";
+export default {
+  data() {
+    return {
+      timer: null,
+      userinfo: [],
+      RoomInfo: [],
+      copyText: "",
+      userid: this.$route.query.userid,
+      data: [],
+      dialogVisible: false,
+      dialogVisible2: false,
+      dialogVisible3: true,
+      deleteVisible1: false,
+      deleteVisible2: false,
+      dialogImgVisible: false,
+      dialogImageUrl: "",
+      remarks: "",
+      goNum: "",
+      numNum: "",
+      file: {},
+      gIndex: "",
+      pIndex: "",
+      proVisible: false,
+      progress: 0,
+      playerOptions: {
+        playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
+        autoplay: false, //如果true,浏览器准备好时开始回放。
+        muted: false, // 默认情况下将会消除任何音频。
+        loop: false, // 导致视频一结束就重新开始。
+        preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
+        language: "zh-CN",
+        aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
+        fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
+        sources: [
+          {
+            type: "video/mp4", //这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目   || "video/ogg"|| "video/webm"
+            src: "", //url地址require("../../assets/media/aaa.mp4")
+          },
+        ],
+        // poster: require("../../assets/tu31.png"), //你的封面地址
+        // poster: dataRes.imgUrl, //你的封面地址
+        notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
+        controlBar: {
+          timeDivider: true, //当前时间和持续时间的分隔符
+          durationDisplay: true, //显示持续时间
+          remainingTimeDisplay: false, //是否显示剩余时间功能
+          fullscreenToggle: true, //全屏按钮
+        },
+      },
+      playerO: {},
+    };
+  },
+  methods: {
+    copy() {
+      this.copyText =
+        this.$refs.inviteT.innerHTML +
+        "\n" +
+        this.$refs.invite1.innerHTML +
+        "\n" +
+        this.$refs.invite2.innerHTML +
+        "\n" +
+        this.$refs.invite3.innerText +
+        "\n" +
+        this.$refs.invite4.innerHTML;
+      var clipboard = new Clipboard(".tag-read");
+      clipboard.on("success", (e) => {
+        console.log("复制成功");
+        clipboard.destroy(); // 释放内存
+      });
+      clipboard.on("error", (e) => {
+        console.log("不支持复制,该浏览器不支持自动复制");
+        clipboard.destroy(); // 释放内存
+      });
+    },
+    handleClose(done) {
+      this.restart();
+      done();
+    },
+    handleClose2(done) {
+      // done();
+    },
+    restart() {
+      this.file = {};
+      this.gIndex = "";
+      this.remarks = "";
+      if (this.$refs.pathClear1) {
+        this.$refs.pathClear1.value = "";
+        this.$refs.pathClear2.value = "";
+      }
+    },
+    deleteGrid(index) {
+      if (this.data[index].userid != this.userid) {
+        this.$message.error("你不是上传人无权限删除");
+        return;
+      }
+      this.$confirm("确定删除吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.data[index] = "";
+          this.updateRoomData();
+          this.$forceUpdate();
+        })
+        .catch(() => {});
+    },
+    updateRoomData() {
+      let params = [
+        {
+          data: JSON.stringify(this.data),
+          idL: this.RoomInfo.id,
+        },
+      ];
+      this.ajax
+        .post(this.$store.state.api + "updateRoomData", params)
+        .then((res) => {})
+        .catch((err) => {});
+    },
+    check(index, type) {
+      this.gIndex = index;
+      if (this.data[index].userid != this.userid && type != 2) {
+        this.$message.error("你不是上传人无权限修改");
+        return;
+      }
+      if (this.data[index] && this.data[index].userid == this.userid) {
+        if (this.data[index].userid != this.userid) {
+          this.$message.error("你不是上传人无权限修改");
+          return;
+        }
+        this.file = {
+          photo: this.data[index].photo,
+          userid: this.data[index].userid,
+          video:
+            this.data[index].video &&
+            this.data[index].video.sources &&
+            this.data[index].video.sources[0].src,
+        };
+        this.remarks = this.data[index].remarks[0].remarks;
+      } else if (this.data[index] && type == 2) {
+        this.file = {
+          userid: this.data[index].userid,
+        };
+        for (var i = 0; i < this.data[index].remarks.length; i++) {
+          if (this.data[index].remarks[i].userid == this.userid) {
+            this.remarks = this.data[index].remarks[i].remarks;
+            break;
+          }
+        }
+        this.remarks = this.remarks ? this.remarks : "";
+      }
+      this.dialogVisible = true;
+    },
+    addEvent(e) {
+      var el = e.currentTarget;
+      el.getElementsByTagName("input")[0].click();
+    },
+    deleteFile(type) {
+      if (type == 1) {
+        this.file.photo = "";
+        this.deleteVisible1 = false;
+        this.$refs.pathClear1.value = "";
+      } else {
+        this.file.video = "";
+        this.deleteVisible2 = false;
+        this.$refs.pathClear2.value = "";
+      }
+      this.$forceUpdate();
+    },
+    addGrid() {
+      if (!this.file.userid || this.file.userid == this.userid) {
+        if (!this.file.video && !this.file.photo) {
+          this.$message.error("至少上传一个视频或者一张图片");
+          return;
+        }
+        if (this.file.photo) {
+          this.data[this.gIndex].photo = this.file.photo;
+        } else {
+          // this.playerOptions.sources[0].src = ""
+          var video = JSON.parse(JSON.stringify(this.playerOptions));
+          video.sources[0].src = this.file.video;
+          this.data[this.gIndex].video = video;
+        }
+        if (this.data[this.gIndex].remarks) {
+          this.data[this.gIndex].remarks[0] = {
+            remarks: this.remarks,
+            userid: this.userinfo.userid,
+            username: this.userinfo.name,
+          };
+        } else {
+          this.data[this.gIndex].remarks = [
+            {
+              remarks: this.remarks,
+              userid: this.userinfo.userid,
+              username: this.userinfo.name,
+            },
+          ];
+        }
+        this.data[this.gIndex].userid = this.userinfo.userid;
+      } else {
+        if (!this.remarks) {
+          this.$message.error("备注不能为空");
+          return;
+        }
+        var a = 0;
+        for (var i = 0; i < this.data[this.gIndex].remarks.length; i++) {
+          if (this.data[this.gIndex].remarks[i].userid == this.userid) {
+            a++;
+            this.data[this.gIndex].remarks[i] = {
+              remarks: this.remarks,
+              userid: this.userinfo.userid,
+              username: this.userinfo.name,
+            };
+            break;
+          }
+        }
+        if (a < 1) {
+          this.data[this.gIndex].remarks.push({
+            remarks: this.remarks,
+            userid: this.userinfo.userid,
+            username: this.userinfo.name,
+          });
+        }
+      }
+
+      this.updateRoomData();
+      this.dialogVisible = false;
+      this.$forceUpdate();
+      this.restart();
+    },
+    beforeUpload(event, type) {
+      // debugger;
+      if (this.file.photo && type != 1) {
+        this.$message.error("只能上传一个视频或者一张图片");
+        this.$refs.pathClear2.value = "";
+        return;
+      } else if (this.file.video && type != 2) {
+        this.$message.error("只能上传一个视频或者一张图片");
+        this.$refs.pathClear1.value = "";
+        return;
+      }
+      var file = event.target.files[0];
+      var credentials = {
+        accessKeyId: "AKIATLPEDU37QV5CHLMH",
+        secretAccessKey: "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",
+      }; //秘钥形式的登录上传
+      window.AWS.config.update(credentials);
+      window.AWS.config.region = "cn-northwest-1"; //设置区域
+
+      var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
+      var _this = this;
+      if (type == 1) {
+        var mediaFormatList = ["png", "jpg", "jpeg"];
+        if (
+          mediaFormatList.indexOf(
+            file.name
+              .split(".")
+              [file.name.split(".").length - 1].toLocaleLowerCase()
+          ) == "-1"
+        ) {
+          _this.$message.error("请上传jpg或者png的图片格式文件");
+          return;
+        }
+      } else {
+        var mediaFormatList = ["mp4"];
+        if (
+          mediaFormatList.indexOf(
+            file.name
+              .split(".")
+              [file.name.split(".").length - 1].toLocaleLowerCase()
+          ) == "-1"
+        ) {
+          _this.$message.error("请上传mp4视频格式文件");
+          return;
+        }
+      }
+      _this.progress = 0;
+      _this.proVisible = true;
+      if (file) {
+        var params = {
+          Key:
+            file.name.split(".")[0] +
+            new Date().getTime() +
+            "." +
+            file.name.split(".")[1],
+          ContentType: file.type,
+          Body: file,
+          "Access-Control-Allow-Credentials": "*",
+          ACL: "public-read",
+        }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
+        var options = {
+          partSize: 2048 * 1024 * 1024,
+          queueSize: 2,
+          leavePartsOnError: true,
+        };
+        bucket
+          .upload(params, options)
+          .on("httpUploadProgress", function (evt) {
+            //这里可以写进度条
+            // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
+            _this.progress = parseInt((evt.loaded * 80) / evt.total);
+          })
+          .send(function (err, data) {
+            this.progress = 100;
+            setTimeout(() => {
+              _this.proVisible = false;
+            }, 1000);
+            if (err) {
+              _this.$message.error("上传失败");
+            } else {
+              if (type == 1) {
+                _this.file.photo = data.Location;
+              } else {
+                _this.file.video = data.Location;
+              }
+              _this.$forceUpdate();
+              console.log(data.Location);
+            }
+          });
+      }
+    },
+    getUser() {
+      let params = { uid: this.userid };
+      this.ajax
+        .get(this.$store.state.api + "getUser", params)
+        .then((res) => {
+          this.userinfo = res.data[0][0];
+          console.log(res.data[0][0]);
+        })
+        .catch((err) => {
+          console.error(err);
+        });
+    },
+    MathRand() {
+      var Num = "";
+      for (var i = 0; i < 6; i++) {
+        Num += Math.floor(Math.random() * 10);
+      }
+      return Num;
+    },
+    checkNum() {
+      this.numNum = this.MathRand();
+      let params = {
+        num: this.numNum,
+      };
+      this.ajax
+        .get(this.$store.state.api + "checkRoomNum", params)
+        .then((res) => {
+          console.log(res.data[0].length);
+          if (res.data[0].length) {
+            this.checkNum();
+          } else {
+            this.addRoom();
+          }
+        })
+        .catch((err) => {
+          console.error(err);
+        });
+    },
+    addRoom() {
+      let params = [
+        {
+          uid: this.userid,
+          n: this.numNum,
+          d: JSON.stringify(this.data),
+        },
+      ];
+      this.ajax
+        .post(this.$store.state.api + "insertRoom", params)
+        .then((res) => {
+          this.$message.success("创建成功");
+          this.selectRoom(this.userid);
+        })
+        .catch((err) => {
+          this.$message.error("创建失败");
+          console.error(err);
+        });
+    },
+    selectRoom(uid) {
+      let params = {
+        uid: uid,
+      };
+      this.ajax
+        .get(this.$store.state.api + "selectRoom", params)
+        .then((res) => {
+          if (res.data[0].length) {
+            this.RoomInfo = res.data[0][0];
+            this.data = JSON.parse(res.data[0][0].data);
+            this.dialogVisible3 = false;
+            this.dialogVisible2 = true;
+            this.timer = setInterval(() => {
+              this.selectRoom2(uid);
+            }, 5000);
+          } else {
+            // this.$message.error("此房间不存在或已被解散");
+          }
+        })
+        .catch((err) => {
+          console.error(err);
+        });
+    },
+    goRoom(uid) {
+      let params = {
+        uid: uid,
+      };
+      this.ajax
+        .get(this.$store.state.api + "selectRoom", params)
+        .then((res) => {
+          if (res.data[0].length) {
+            this.$message.success("加入成功");
+            this.RoomInfo = res.data[0][0];
+            this.data = JSON.parse(res.data[0][0].data);
+            this.dialogVisible3 = false;
+            this.dialogVisible2 = true;
+            this.timer = setInterval(() => {
+              this.selectRoom2(uid);
+            }, 5000);
+          } else {
+            this.$message.error("此房间不存在或已被解散");
+          }
+        })
+        .catch((err) => {
+          console.error(err);
+        });
+    },
+    selectRoom2(uid) {
+      let params = {
+        uid: uid,
+      };
+      this.ajax
+        .get(this.$store.state.api + "selectRoom", params)
+        .then((res) => {
+          this.RoomInfo = res.data[0][0];
+          this.data = JSON.parse(res.data[0][0].data);
+        })
+        .catch((err) => {
+          console.error(err);
+        });
+    },
+  },
+  beforeDestroy() {
+    clearInterval(this.timer);
+  },
+  created() {
+    for (var i = 0; i < 16; i++) {
+      this.data.push("");
+    }
+    this.getUser();
+    this.selectRoom(this.userid);
+  },
+};
+</script>
+
+<style scoped>
+.g_body {
+  width: 100%;
+  height: 100%;
+  background: #fff;
+  border-radius: 5px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-wrap: wrap;
+  position: relative;
+  min-height: 750px;
+}
+.coin {
+  position: absolute;
+  width: 50px;
+  z-index: 1;
+}
+.coin1 {
+  top: 10px;
+  left: 20px;
+}
+.coin2 {
+  top: 10px;
+  right: 20px;
+}
+.coin3 {
+  bottom: 10px;
+  left: 20px;
+}
+.coin4 {
+  bottom: 10px;
+  right: 20px;
+}
+.g_box {
+  background: rgb(227 228 232);
+  width: calc(100% / 4 - 15px);
+  height: calc(100% / 4 - 15px);
+  margin: 5px;
+  border-radius: 5px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  /* overflow: hidden; */
+  position: relative;
+}
+.photo_img {
+  width: 100%;
+  height: 100%;
+  border-radius: 5px;
+}
+.g_box .add_img {
+  width: 100px;
+  display: none;
+}
+.g_box:hover .add_img {
+  display: block;
+}
+.look_notice >>> .el-dialog__header {
+  padding: 10px 20px;
+  text-align: center;
+  background: #32455b;
+}
+.look_notice >>> .el-dialog__title {
+  font-size: 14px !important;
+  color: #fff !important;
+}
+.look_notice >>> .el-dialog__headerbtn {
+  font-size: 20px !important;
+  top: 10px;
+}
+.look_notice >>> .el-form-item__label {
+  margin-left: 65px;
+}
+.look_notice >>> .el-form-item {
+  display: flex;
+}
+.look_notice >>> .el-form-item__content {
+  margin: 0 !important;
+}
+.look_notice >>> .el-dialog__footer {
+  text-align: center !important;
+}
+.look_notice >>> .el-dialog {
+  min-width: 450px;
+  background: #f3f3f3;
+}
+.notice_content {
+  width: 100%;
+  word-wrap: break-word;
+  word-break: break-all;
+  overflow: hidden;
+  font-size: 18px;
+  line-height: 35px;
+  text-indent: 35px;
+  min-width: 385px;
+}
+.roomBtn {
+  background: rgb(112 183 79);
+  /* padding: 0 !important; */
+  border: none;
+}
+.close {
+  width: 150px;
+  height: 40px;
+  border-radius: 30px;
+  line-height: 30px;
+  font-size: 14px;
+  background: rgb(112 183 79);
+  padding: 0 !important;
+  border: none;
+}
+.header-title {
+  display: flex;
+}
+.logoImg {
+  width: 30px;
+}
+.logoImg > img {
+  width: 100%;
+  height: 100%;
+}
+.title_add_student {
+  /* margin: 0 auto; */
+  color: #fff;
+}
+
+.grid_member {
+  position: absolute;
+  background: #fff;
+  right: 70px;
+  top: 18px;
+  min-width: 320px;
+  height: 50px;
+  border-radius: 5px;
+  padding-right: 10px;
+  display: flex;
+  align-items: center;
+  z-index: 1;
+  user-select: none;
+}
+
+.member_imgbox {
+  display: flex;
+  align-items: center;
+  margin: 0 10px;
+  width: 185px;
+}
+.member_img {
+  height: 40px;
+  width: 40px;
+  border-radius: 50px;
+  overflow: hidden;
+  display: block;
+  background: aliceblue;
+}
+.member_img + .member_img {
+  margin-left: 8px;
+}
+
+.upload_box {
+  border-radius: 5px;
+  background: #fff;
+  position: relative;
+}
+
+.upload_box .list_img {
+  position: absolute;
+  top: 10px;
+  left: 10px;
+}
+
+.upload_box .upload_content {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 50px 0 20px;
+}
+.upload_box .upload_content div {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  position: relative;
+}
+.delete_img {
+  position: absolute !important;
+  width: 25px;
+  transform: translate(50%, -50%);
+  top: 0;
+  z-index: 1;
+  right: 0;
+}
+.upload_img {
+  width: 306px;
+  height: 206px;
+}
+.textarea_box {
+  background: #fff;
+  margin-top: 20px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 20px 0;
+}
+
+.textarea_title {
+  width: 95%;
+  margin: 0 auto;
+  font-size: 16px;
+}
+
+.textarea_content {
+  width: 95%;
+  margin: 10px auto 0;
+  border-radius: 10px;
+}
+
+.textarea_content >>> .el-textarea__inner {
+  background: rgb(243, 243, 243);
+  border: none;
+}
+
+.invite_dialog >>> .el-dialog__body {
+  padding: 15px 20px;
+}
+.room_dialog >>> .el-dialog {
+  margin-top: 50vh !important;
+  transform: translateY(-50%);
+  min-width: unset;
+}
+.invite_box {
+  font-size: 16px;
+  background: #fff;
+  border-radius: 5px;
+  padding: 30px 0 150px;
+}
+
+.invite_title {
+  width: 90%;
+  margin: 0 auto 20px;
+}
+
+.line {
+  width: 95%;
+  border-top: 1px solid rgb(230, 230, 230);
+  margin: 0 auto;
+}
+
+.invite_link {
+  width: 90%;
+  margin: 20px auto 30px;
+}
+.invite_link div + div {
+  margin-top: 15px;
+}
+
+.invite_num {
+  width: 90%;
+  margin: 0 auto 50px;
+}
+.invite_num span {
+  color: rgb(112 183 79);
+  font-size: 28px;
+}
+.addRoom_box {
+  background: #fff;
+  padding: 30px 20px;
+  border-radius: 5px;
+}
+.room_b {
+  margin: 5px 0 30px;
+  /* width:300px; */
+}
+.room_btn {
+  display: flex;
+  justify-content: space-around;
+}
+
+.mask {
+  background-color: rgba(0, 0, 0, 0);
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 20000;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.progressBox {
+  width: 500px;
+  height: 180px;
+  background: #fff;
+  border-radius: 10px;
+  box-shadow: 0 0 6px 1px #bfbfbf;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+}
+.progressBox .lbox {
+  height: 100px;
+  font-size: 19px;
+  display: flex;
+  align-items: center;
+}
+
+.progressBox .lbox img {
+  width: 40px;
+  margin-right: 20px;
+}
+
+.progressBox >>> .el-progress-bar__outer {
+  background-color: #d1dfff !important;
+}
+
+.top-popover {
+  position: absolute;
+  top: -50px;
+  left: 0;
+  background: #fff;
+  display: flex;
+  height: 45px;
+  align-items: center;
+  padding: 0px 10px 0 20px;
+  border-radius: 5px;
+  box-shadow: 0 1px 3px 1px rgb(202, 202, 202);
+  z-index: 10;
+  transition: 0.5s all;
+  opacity: 0;
+  visibility: hidden;
+}
+
+.top-popover .pChild {
+  display: flex;
+  align-items: center;
+}
+.top-popover .pChild + .pChild {
+  margin-left: 10px;
+}
+
+.left-popover {
+  position: absolute;
+  top: 0px;
+  right: -65px;
+  background: #fff;
+  display: flex;
+  width: 45px;
+  align-items: center;
+  padding: 10px 5px 20px;
+  border-radius: 5px;
+  box-shadow: 0 1px 3px 1px rgb(202, 202, 202);
+  z-index: 10;
+  flex-direction: column;
+  justify-content: center;
+  transition: 0.5s all;
+  opacity: 0;
+  visibility: hidden;
+}
+.left-popover .pChild {
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+}
+.left-popover .pChild span {
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  text-align: center;
+}
+.left-popover .pChild + .pChild {
+  margin-top: 10px;
+}
+
+.right-popover {
+  position: absolute;
+  top: 0px;
+  left: -65px;
+  background: #fff;
+  display: flex;
+  width: 45px;
+  align-items: center;
+  padding: 10px 5px 20px;
+  border-radius: 5px;
+  box-shadow: 0 1px 3px 1px rgb(202, 202, 202);
+  z-index: 10;
+  flex-direction: column;
+  justify-content: center;
+  transition: 0.5s all;
+  opacity: 0;
+  visibility: hidden;
+}
+.right-popover .pChild {
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+}
+.right-popover .pChild span {
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  text-align: center;
+}
+.right-popover .pChild + .pChild {
+  margin-top: 10px;
+}
+
+.visibleO {
+  opacity: 1 !important;
+  visibility: visible !important;
+}
+
+.remark-bottom-popover {
+  position: absolute;
+  bottom: 0px;
+  left: 0;
+  background: #fff;
+  max-height: 150px;
+  width: 100%;
+  padding: 10px 20px;
+  border-radius: 5px;
+  box-shadow: 0 1px 3px 1px rgb(202, 202, 202);
+  z-index: 10;
+  transition: 0.5s all;
+  box-sizing: border-box;
+  transform: translateY(calc(100% + 5px));
+  overflow: auto;
+  opacity: 0;
+  visibility: hidden;
+}
+
+.Rbox {
+  display: flex;
+  word-break: break-all;
+}
+
+.Rbox + .Rbox {
+  border-top: 1px solid rgb(233, 233, 233);
+  margin-top: 10px;
+  padding-top: 10px;
+}
+
+.Rportal {
+  height: 45px;
+  min-width: 45px;
+  border-radius: 50px;
+  overflow: hidden;
+  display: block;
+  background: aliceblue;
+  user-select: none;
+}
+
+.RContent {
+  margin-left: 10px;
+}
+
+.RContent div:nth-child(1) {
+  user-select: none;
+  color: rgb(80, 80, 80);
+  margin-bottom: 5px;
+  font-size: 14px;
+}
+.RContent div:nth-child(2) {
+  font-family: "微软雅黑";
+  font-size: 15px;
+}
+
+.remark-right-popover {
+  position: absolute;
+  top: 0;
+  right: 0;
+  background: #fff;
+  max-height: 100%;
+  width: 300px;
+  padding: 10px 20px;
+  border-radius: 5px;
+  box-shadow: 0 1px 3px 1px rgb(202, 202, 202);
+  z-index: 10;
+  transition: 0.5s all;
+  box-sizing: border-box;
+  transform: translateX(calc(100% + 5px));
+  overflow: auto;
+  opacity: 0;
+  visibility: hidden;
+}
+
+.remark-left-popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  background: #fff;
+  max-height: 100%;
+  width: 300px;
+  padding: 10px 20px;
+  border-radius: 5px;
+  box-shadow: 0 1px 3px 1px rgb(202, 202, 202);
+  z-index: 10;
+  transition: 0.5s all;
+  box-sizing: border-box;
+  transform: translateX(calc(-100% - 5px));
+  overflow: auto;
+  opacity: 0;
+  visibility: hidden;
+}
+</style>

+ 74 - 0
src/components/pages/demo.vue

@@ -0,0 +1,74 @@
+<template>
+    <div class="pop-over">
+        <a @click="toggleOpen" class="pop-button" href="javascript: void(0);">
+            123456
+        </a>
+        <ul v-clickoutside="close" v-show="open" class="pop-list">
+            <li>选项1</li>
+            <li>选项2</li>
+            <li>选项3</li>
+            <li>选项4</li>
+        </ul>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'PopOver',
+    props: ['buttonText'],
+    data() {
+        return {
+            open: false
+        }
+    },
+    methods: {
+        toggleOpen: function() {
+            this.open = !this.open;
+        },
+        close: function(e) {
+            if(this.$el.contains(e.target)) return;
+            this.open = false;
+        }
+    },
+    directives: {
+        clickoutside: {
+            bind: function (el, binding, vnode) {
+                const documentHandler = function (e) {
+                    if (!vnode.context || el.contains(e.target)) return;
+                    binding.value(e);
+                };
+
+                setTimeout(() => {
+                    document.addEventListener('click', documentHandler);
+                }, 0);
+            }
+        }
+    }
+}
+</script>
+
+<style scoped>
+.pop-over {
+    position: relative;
+    width: 100%;
+    height: 100%;
+}
+.pop-button {
+    position: relative;
+    width: 100%;
+    height: 100%;
+    text-decoration:none;
+    color: inherit;
+}
+.pop-list {
+    position: absolute;
+    left: 0;
+    top: 0;
+}
+.pop-list li {
+    width: 100%;
+    height: 100%;
+    padding: 8px 3px;
+    list-style:none;
+}
+</style>

+ 19 - 1
src/main.js

@@ -12,8 +12,11 @@ import VideoPlayer from 'vue-video-player'
 import 'video.js/dist/video-js.css' //videoJs的样式
 import 'vue-video-player/src/custom-theme.css' //vue-video-player的样式
 import VueCookies from 'vue-cookies'
+import Viewer from 'v-viewer'
+import 'viewerjs/dist/viewer.css'
 
-Vue.use(VideoPlayer).use(VueCookies)
+
+Vue.use(VideoPlayer).use(VueCookies).use(Viewer)
 Vue.config.productionTip = false
 Vue.prototype.$store = store; // 将store实例挂在vue原型上
 Vue.prototype.ajax = ajax
@@ -29,6 +32,21 @@ Vue.prototype.openLoading = function(target) {
     return loading;
 }
 Vue.prototype.$qs = qs
+Viewer.setDefaults({
+    'inline': false, //启用inline模式
+    'button': false, //显示右上角关闭按钮
+    'navbar': false, //显示缩略图导航
+    'title': false, //显示当前图片的标题
+    'toolbar': true, //显示工具栏
+    'tooltip': true, //显示缩略百分比
+    'movable': true, //图片是否可移动
+    'zoomable': true, //图片是否可缩放
+    'rotatable': true, //图片是否可旋转
+    'scalable': true, //图片是否可反转
+    'transition': true, //使用css3过度
+    'fullscreen': false, //播放时是否全屏
+    'keyboard': true, //
+})
 
 /* eslint-disable no-new */
 new Vue({

+ 18 - 0
src/router/index.js

@@ -15,6 +15,8 @@ import ask from '@/components/pages/ask'
 import addCourse from '@/components/pages/addCourse'
 import library from '@/components/pages/library'
 import note from '@/components/pages/note'
+import Grid from '@/components/pages/Grid'
+import demo from '@/components/pages/demo'
 
 Vue.use(Router).use(ElementUI)
 
@@ -129,6 +131,22 @@ export default new Router({
             meta: {
                 requireAuth: '' // 不需要鉴权
             }
+        },
+        {
+            path: '/Grid',
+            name: 'Grid',
+            component: Grid,
+            meta: {
+                requireAuth: '' // 不需要鉴权
+            }
+        },
+        {
+            path: '/demo',
+            name: 'demo',
+            component: demo,
+            meta: {
+                requireAuth: '' // 不需要鉴权
+            }
         }
     ]
 })