tangning 3 سال پیش
والد
کامیت
2362722818

+ 2 - 1
.env

@@ -1,2 +1,3 @@
+VUE_APP_PREFIX=
 VUE_APP_DOMAIN=https://www.4dkankan.com
-VUE_APP_PREFIX=
+

+ 1 - 0
src/assets/style/public.scss

@@ -208,6 +208,7 @@ body {
   }
   &.disable {
     color: #BFBFBF !important;
+    pointer-events: none;
   }
 
   &:not(.disable) {

+ 20 - 1
src/main.js

@@ -10,6 +10,20 @@ const app = createApp(App);
 app.use(router)
 app.use(ElementPlus, { locale })
 app.mount('#app')
+// 挂在全局方法
+app.config.globalProperties.$power = (fun,val) =>{
+    let replacelist = {
+        'organizationlist':'organization',
+    }
+    let routeName = router.currentRoute._value.name
+    let roleKeyList = user._value.roleKeyList
+    let rokeName =  `${replacelist[routeName] || routeName}:${val}`
+    if(roleKeyList.includes(rokeName)){
+        fun()
+    }else{
+        console.warn('暂无权限',routeName)
+    }
+}
 app.directive('power',{
     mounted(el,binding) {
         let replacelist = {
@@ -19,7 +33,12 @@ app.directive('power',{
         let roleKeyList = user._value.roleKeyList
         let rokeName =  `${replacelist[routeName] || routeName}:${binding.value}`
         if(!roleKeyList.includes(rokeName)){
-            el.parentNode&&el.parentNode.removeChild(el);
+                var klses = el.className;
+                console.log('klses',klses)
+                el.className = klses + ' disable'
+                
+                // el.onclick=null;
+            // el.parentNode&&el.parentNode.removeChild(el);
         }
     }
 })

+ 9 - 0
src/request/config.js

@@ -120,6 +120,8 @@ export const deleteScene = '/web/scene/delete'
 export const getCameraList = '/web/camera/getUserCameraList'
 // 获取相机选项
 export const getCameraOptions = '/web/camera/getUserCameraList?pageNum=1&pageSize=100000&type=1'
+// 获取相机选项(自己)
+export const getCameraListByUser = '/web/camera/getCameraListByUser?pageNum=1&pageSize=100000'
 // 添加相机
 export const insertCamera = '/web/camera/bindNew'
 // 添加相机管理人员
@@ -192,6 +194,13 @@ export const uploadAttachImage = '/web/fireProject/uploadImage'
 /** ------------------------------------------ */
 
 
+  // 下载校验
+  export const checkHasDownload = '/web/scene/checkDownload/'
+  // 下载获取进度条
+  export const getDownloadProcess = '/web/scene/downloadProcess'
+  // 下载
+  export const downloadScene = '/web/scene/downloadScene/'
+
 // 不需要登录就能请求的接口
 export const notLoginUrls = [userLogin, getCode, sendUserMsg, userReg, updatePsw, getCompanyList, fireDetailByPsw, getAttachListByPsw]
 // 需要用表单提交的数据

+ 6 - 0
src/router/index.js

@@ -12,6 +12,7 @@ import HomeView from '@/view/home'
 import VRModelView from '@/view/vrmodel'
 // import TeachingView from '@/view/teaching'
 import CameraView from '@/view/camera'
+import RoleView from '@/view/role'
 import UserView from '@/view/user'
 import InitiatorView from '@/view/scene/Initiator'
 import VisitorView from '@/view/scene/visitor'
@@ -96,6 +97,11 @@ export const routes = [
         path: 'teaching',
         component: DispatchView,
         meta: { title: '教学平台' }
+      },{
+        name: 'role',
+        path: 'role',
+        component: RoleView,
+        meta: { title: '角色管理' }
       },
       {
         name: 'user',

+ 3 - 1
src/state/navs.js

@@ -3,7 +3,7 @@ import { attach } from '@/constant/view'
 import user from './user'
 import { watch, ref } from 'vue'
 // 所有权限都有的router
-export const MUST_JOIN_NAVS = ['viewLayout', 'login', 'register', 'forget', 'scene', 'organization', 'sceneVisitor']
+export const MUST_JOIN_NAVS = ['viewLayout', 'login', 'register', 'forget', 'scene', 'sceneVisitor']
 // router对应的icon
 const ICON_MAP = { 
   estate: 'iconfire_scenes', 
@@ -14,6 +14,7 @@ const ICON_MAP = {
   organization: 'el-icon-guide', 
   teaching: 'iconfire_study',
   user: 'iconfire_user',
+  role:'iconfire_user',
 }
 
 const getNames = (config) => {
@@ -43,6 +44,7 @@ const _getNavs = (items, notJoinNavs) => {
       }
     }
   }
+  console.log('_getNavs',ret);
   return ret
 }
 

+ 1 - 1
src/state/user.js

@@ -50,7 +50,7 @@ export const setPermission = async (val,permissions) => {
   let power = []
   if (val !== void 0){
     power = permissions&&permissions.map(element => element.replace('dept','organization').replace('scene','vrmodel'));
-    let pubPermission = ['home', 'vrmodel', 'camera', 'teaching', 'dispatch'].map(key => ({
+    let pubPermission = ['home', 'vrmodel', 'camera', 'teaching', 'dispatch','role','organization'].map(key => ({
       children: [
         { resourceKey: key + ':select' },
         { resourceKey: key + ':update' },

+ 1 - 5
src/view/camera/index.vue

@@ -46,11 +46,7 @@
       </el-table-column>
       <el-table-column label="操作" v-slot:default="{ row }" v-if="auth.unbind || auth.update">
         <!-- <span class="oper-span" @click="oper.readyUpdate(row)" v-if="auth.update">编辑</span> -->
-        <span class="oper-span" 
-          @click="unbindCamrea(row)" 
-          v-if="auth.delete" 
-          v-power="'unbind'"
-          style="color: var(--primaryColor)">
+        <span class="oper-span" @click="unbindCamrea(row)" v-power="'unbind'" style="color: var(--primaryColor)">
          解绑
         </span>
       </el-table-column>

+ 10 - 13
src/view/dispatch/index.vue

@@ -104,23 +104,20 @@
       <el-table-column label="项目状态" v-slot:default="{ row }" >
         {{row.status === 0 ? '未认定' : '已认定'}}
       </el-table-column>
+        <!--详情 v-if="user.info.id == row.creatorId || user.roleKey == 'admin-dept'" 带看 v-if="row.sceneNum && !isTeaching" 
+        撤销 click=" user.roleKey === ADMIN_USER_ID && “  :class="{disable: user.roleKey !== ADMIN_USER_ID}" 分享 :class="{disable: !row.sceneNum}"
+        -->
       <el-table-column label="操作" v-slot:default="{ row }" :width="isTeaching ? 280 : 320">
-        <span class="oper-span" @click="queryDetail(row)" v-power="'view'">详情</span>
-        <span class="oper-span" @click="archivesHandle(row)" v-power="'readFile'" v-if="user.info.id == row.creatorId || user.roleKey == 'admin-dept'">档案</span>
-        <span class="oper-span" @click="queryScene(row)" v-power="'readFile'" >场景</span>
-        <span class="oper-span" v-if="row.sceneNum && !isTeaching" v-power="'look'" @click="takeLook(row)">带看</span>
+        <span class="oper-span" @click="queryDetail(row)" :class="{disable: (user.info.id == row.creatorId || user.roleKey == 'admin-dept')}" v-power="'view'">详情</span>
+        <span class="oper-span" @click="archivesHandle(row)" v-power="'readFile'" >档案</span>
+        <!-- <span class="oper-span" @click="queryScene(row)" v-power="'readFile'" >场景</span> -->
+        <span class="oper-span" v-power="'look'" :class="{disable: !row.sceneNum}" v-if="!isTeaching" @click="takeLook(row)">带看</span>
         <template v-if="isTeaching">
-          <span class="oper-span" @click="leaveMsgHandle(row)" v-power="'message'">留言</span>
-          <span class="oper-span" 
-            @click=" user.roleKey === ADMIN_USER_ID && revokeTeaching(row)" 
-            :class="{disable: user.roleKey !== ADMIN_USER_ID}"
-            style="color: var(--primaryColor)">
-            撤销
-          </span>
+          <span class="oper-span" v-power="'message'" @click="leaveMsgHandle(row)">留言</span>
+          <span class="oper-span" v-power="'cancel'" @click="revokeTeaching(row)" style="color: var(--primaryColor)">撤销</span>
         </template>
         <template v-else-if="user.info.id == row.creatorId || user.roleKey == 'admin-dept'">
-          
-          <span class="oper-span" v-power="'share'" @click="row.sceneNum && shareHandle(row)" :class="{disable: !row.sceneNum}">分享</span>
+          <span class="oper-span" v-power="'share'" @click="row.sceneNum && shareHandle(row)">分享</span>
           <span class="oper-span" v-power="'edit'" @click="auth.update && user.info.deptId == row.deptId && editInfo(row)" :class="{disable: !(auth.update && user.info.deptId == row.deptId)}">编辑</span>
           <span class="oper-span" 
             @click="auth.delete && user.info.deptId == row.deptId && dataList.delete(row)" 

+ 1 - 1
src/view/layout/index.vue

@@ -3,7 +3,7 @@
     <ly-top class="top" v-if="!isSystem" />
     <div class="content">
       <router-view v-slot="{ Component }" v-if="isSystem">
-        <component :is="Component" />{{isSystem}}
+        <component :is="Component" />
       </router-view>
       <template v-else>
         <ly-slide class="slide" />

+ 569 - 0
src/view/role/index.vue

@@ -0,0 +1,569 @@
+<template>
+  <div>
+    <com-head :options="headList">
+      <el-form label-width="84px" inline="true">
+        <el-form-item label="角色名称:">
+          <el-input
+            v-model="search.state.nickName"
+            placeholder="请输入"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="所属架构:">
+          <com-company v-model="search.state.deptId" />
+        </el-form-item>
+        <el-form-item class="searh-btns">
+          <el-button type="primary" @click="search.submit">查询</el-button>
+          <el-button type="primary" plain @click="search.reset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </com-head>
+
+    <div class="body-layer" style="padding-top: 8px">
+      <div class="body-but">
+        <el-button type="primary" @click="newAddclick">新增角色</el-button>
+        <!-- <h3 style="visibility: hidden;">用户列表</h3> -->
+      </div>
+
+      <el-table
+        class="user-table"
+        ref="multipleTable"
+        :data="dataList.state"
+        tooltip-effect="dark"
+        style="width: 100%;max-height:480px"
+        @row-click="selectRow"
+      >
+        <el-table-column label="序号" width="55" v-slot:default="{ $index }">
+          <div style="text-align: center">
+            {{ pag.state.size * (pag.state.currPage - 1) + $index + 1 }}
+          </div>
+        </el-table-column>
+        <el-table-column label="角色名称" prop="userName"></el-table-column>
+        <el-table-column label="角色类型" prop="nickName"></el-table-column>
+        <el-table-column
+          label="所属架构"
+          prop="departmentName"
+        ></el-table-column>
+        <el-table-column label="创建人" prop="roleName"></el-table-column>
+        <el-table-column label="创建时间" prop="roleName"></el-table-column>
+        <el-table-column label="操作" v-slot:default="{ row }">
+          <!-- v-if="auth.update || auth.updatePwd || auth.delete"  v-if="auth.update"-->
+          <!-- row.roleKey == roleKey[user.roleKey] -->
+          <template v-if="user.roleKey !== 'admin-ordinary'">
+            <span class="oper-span" @click="delInfo(row)" v-power="'view'"
+              >查看</span
+            >
+            <span class="oper-span" @click="updateInfo(row)" v-power="'edit'"
+              >编辑</span
+            >
+            <!-- v-if="auth.delete" -->
+            <span
+              class="oper-span"
+              style="color: var(--primaryColor)"
+              @click="delInfo(row)"
+              v-power="'del'"
+              >删除</span
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <com-pagination
+        @size-change="pag.sizeChange"
+        @current-change="pag.currentChange"
+        :current-page="pag.state.currPage"
+        :page-size="pag.state.size"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="pag.state.total"
+      />
+    </div>
+
+    <com-dialog
+      title="编辑用户"
+      v-model:show="oper.state.show"
+      @submit="operItem"
+      width="540"
+    >
+      <el-form ref="form" :model="form" label-width="90px" class="user-from">
+        <el-form-item label="用户姓名" class="mandatory">
+          <el-input
+            v-model.trim="editName"
+            placeholder="请输入"
+            maxlength="15"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+    </com-dialog>
+
+    <com-dialog
+      title="新增角色"
+      @quit="
+        newData = {};
+        deptIdList = [];
+      "
+      v-model:show="newShow"
+      @submit="newSubmit"
+      width="540"
+    >
+      <el-form ref="form" :model="form" label-width="90px" class="user-from">
+        <el-form-item label="角色名称:" class="mandatory">
+          <el-input
+            maxlength="15"
+            v-model="newData.nickName"
+            placeholder="请输入"
+          ></el-input>
+        </el-form-item>
+        <el-form-item
+          label="所属架构:"
+          v-if="user.roleKey !== 'admin-ordinary'"
+        >
+          <el-input
+            maxlength="15"
+            disabled="true"
+            v-model="newData.userRokename"
+            placeholder="请输入"
+          ></el-input>
+        </el-form-item>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="grid-content bg-purple">
+              <div class="title">功能权限</div>
+              <el-tree
+                ref="tree"
+                :data="roledata"
+                highlight-current
+                show-checkbox
+                :current-node-key="nodekey"
+                node-key="id"
+                @check="handleClick"
+                @node-click="handClick"
+                :props="{ children: 'children', label: 'label' }"
+              />
+              <!-- @current-change="handcurrentChange" 
+                @check-change="handcheckchange"-->
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="grid-content bg-purple">
+              <div class="title">数据权限</div>
+              <el-radio-group
+                v-if="selectShow"
+                v-model="radio"
+                @change="radioChange"
+              >
+                <el-radio
+                  style="line-height:28px"
+                  v-for="item in roleSelct"
+                  :key="item.id"
+                  :label="item.id"
+                  size="medium"
+                  >{{ item.value }}</el-radio
+                >
+              </el-radio-group>
+            </div>
+          </el-col>
+        </el-row>
+      </el-form>
+    </com-dialog>
+  </div>
+</template>
+
+<script>
+import { reactive, ref, toRefs, onMounted } from "vue";
+import getTableState from "@/state/tableRef";
+import comDialog from "@/components/dialog";
+import comHead from "@/components/head";
+import comCompany from "@/components/company-select";
+import comPagination from "@/components/pagination";
+// import roleCompany from "@/components/role-select";
+// import comSelect from "@/components/company-select";
+import auth from "@/state/viewAuth";
+import user from "@/state/user";
+import axios from "axios";
+// import { encryption } from "@/util";
+import { PHONE, EPSW } from "@/constant/REG";
+import { ADMIN_USER_ID } from "@/constant";
+
+import {
+  getTreeselect,
+  getUserList,
+  updateUser,
+  deleUser,
+  changeUserStatus,
+  userAdd,
+  userEdit,
+} from "@/request/config";
+import { getApp } from "../../app";
+
+export default {
+  name: "user",
+  setup() {
+    const flag = ref(true);
+    const state = getTableState({
+      getUrl: getUserList,
+      updateUrl: updateUser,
+      operAttr: {
+        nickName: "",
+        userName: "",
+        deptId: "",
+        password: "",
+        confirmPwd: "",
+        roleId: "",
+        maxlevel: 1,
+      },
+      searchAttr: { nickName: "", status: "", deptId: "" },
+    });
+    const headList = ref([{ name: "角色管理", value: 2 }]);
+    const roleKey = ref({
+      admin: "admin-dept",
+      "admin-dept": "admin-ordinary",
+    });
+    const operRoleId = ref("");
+    const updateInfo = (row) => {
+      if (!row.status) {
+        return getApp().$message.error("请先启用用户", "提示");
+      }
+      data.editName = row.nickName;
+      operRoleId.value = row.roleId;
+      state.oper.value.readyUpdate(row);
+    };
+    const delInfo = async (row) => {
+      let isOk = await getApp().$confirm(
+        "用户被删除后,无法登陆使用,无法编辑场景(可将该用户关联的相机绑定到其他管理员),确认要删除组织吗?",
+        "删除"
+      );
+      if (isOk) {
+        await axios.post(deleUser, { id: row.id });
+        getApp().$message({ message: "操作成功", type: "success" });
+        state.search.value.submit();
+      }
+    };
+    const getTreedata = async () => {
+      let res = await axios.get(getTreeselect, {});
+      data.treedata = res.data;
+      console.log("data.treedata", data.treedata);
+      // getApp().$ref.treeBox.setCurrentKey(user.info.id)
+    };
+    const newAddclick = () => {
+      data.newShow = true;
+    };
+    const data = reactive({
+      newShow: false,
+      editName: "", //修改编辑用户名称
+      deptIdList: [],
+      radio: 1,
+      radioData: {},
+      nodekey: "",
+      selectShow: false,
+      roleSelct: [
+        { id: 1, value: "全部数据" },
+        { id: 2, value: "所属组织及下级组织数据" },
+        { id: 3, value: "所属组织内部数据" },
+        { id: 4, value: "个人创建数据" },
+      ],
+      selectData: {},
+      roledata: [
+        {
+          id: 1,
+          label: "场景管理",
+          value: true,
+          children: [
+            {
+              id: 14,
+              label: "查看",
+              value: false,
+            },
+            {
+              id: 19,
+              label: "删除",
+              value: false,
+            },
+            {
+              id: 10,
+              label: "编辑",
+              value: false,
+            },
+          ],
+        },
+        {
+          id: 2,
+          label: "火调管理",
+          value: true,
+          children: [
+            {
+              id: 24,
+              label: "查看",
+              value: false,
+            },
+            {
+              id: 29,
+              label: "删除",
+              value: false,
+            },
+            {
+              id: 20,
+              label: "编辑",
+              value: false,
+            },
+          ],
+        },
+      ],
+      treedata: [],
+      newData: {
+        userName: "",
+        userId: "",
+        userRokename: "全部数据",
+        password: "",
+        userRole: "",
+      },
+    });
+    onMounted(async () => {
+      getTreedata();
+    });
+    return {
+      ...state,
+      ...toRefs(data),
+      flag,
+      getTreedata,
+      headList,
+      delInfo,
+      updateInfo,
+      auth,
+      user,
+      operRoleId,
+      ADMIN_USER_ID,
+      roleKey,
+      newAddclick,
+    };
+  },
+  methods: {
+    //@check事件
+    handleClick(data, checked) {
+      console.log(checked);
+      console.log(data);
+      let CurrentKey = this.$refs.tree.getCurrentKey();
+      if (
+        checked.checkedKeys.includes(data.id) &&
+        !data.value &&
+        CurrentKey == data.id
+      ) {
+        this.setselectShow(data, data.id);
+      } else {
+        this.selectShow = null;
+      }
+    },
+    radioChange(val) {
+      let id = this.selectShow.id;
+      this.radioData[id] = val;
+      console.log("radioChange", val);
+    },
+    handClick(data) {
+      let checkedNodes = this.$refs.tree.getCheckedNodes();
+      let selectShow = checkedNodes.find((ele) => data.id == ele.id) || null;
+      if (!data.value) {
+        this.setselectShow(selectShow, data.id);
+      }
+      console.log("handClick", this.selectShow);
+    },
+    setselectShow(selectShow, id) {
+      this.selectShow = selectShow;
+      this.radio = this.radioData[id] || 1;
+    },
+    handcurrentChange(a, b) {
+      console.log("handcurrentChange", a, b);
+      console.log("ref", this.selectShow);
+    },
+    changeDeptId(val) {
+      this.newData.deptId = val && val[val.length - 1];
+    },
+    async newSubmit() {
+      console.log("newSubmit", this.user, this.newData);
+      if (!this.newData.nickName) {
+        return this.$message.error("请输入用户姓名", "提示");
+      }
+      if (!this.newData.roleId) {
+        return this.$message.error("请选择用户角色", "提示");
+      }
+      console.log(PHONE.REG.test(this.newData.userName), this.newData.userName);
+      if (!this.newData.userName) {
+        return this.$message.error("请选择用户账户", "提示");
+      } else if (!PHONE.REG.test(this.newData.userName)) {
+        return this.$message.error(PHONE.tip, "提示");
+      }
+      if (!this.newData.psw) {
+        return this.$message.error("请输入登录密码", "提示");
+      } else if (!EPSW.REG.test(this.newData.psw)) {
+        return this.$message.error(EPSW.tip, "提示");
+      }
+      let apiinfo = await axios.post(userAdd, {
+        nickName: this.newData.nickName,
+        password: this.newData.psw,
+        userName: this.newData.userName,
+        roleId: this.newData.roleId,
+        deptId: this.newData.deptId,
+        // ...this.newData
+      });
+      this.$message({ message: apiinfo.msg || "成功", type: "success" });
+      // await this.$confirm('每个组织只能创建一个总管理员~', '新增用户')
+      this.newData = {};
+      this.deptIdList = [];
+      this.newShow = false;
+      this.dataList.refer();
+    },
+    async operItem() {
+      if (!this.editName) {
+        return this.$message.error("请输入用户名称", "提示");
+      }
+      console.log("state.oper", this.oper);
+      await axios.post(userEdit, {
+        id: this.oper.state.id,
+        nickName: this.editName,
+      });
+      this.$message({ message: "编辑成功", type: "success" });
+      this.oper.reset();
+      this.editName = "";
+      this.dataList.refer();
+    },
+    async changeUserStatus(row, d) {
+      let msg = row.status
+        ? `用户被禁用后,无法登录使用,无法编辑场景(可将该用户关联的相机绑定到其它管理员)。确定要禁用吗?`
+        : `用户被启用后,可正常登录使用。确定要启用吗?`;
+      try {
+        if (d || (await this.$confirm(msg, "提示"))) {
+          await axios.post(changeUserStatus, {
+            status: Number(!row.status),
+            userId: row.id,
+          });
+          this.dataList.refer();
+        }
+        return true;
+      } catch {
+        return false;
+      }
+    },
+  },
+  components: {
+    "com-dialog": comDialog,
+    "com-head": comHead,
+    "com-company": comCompany,
+    // "com-role": roleCompany,
+    "com-pagination": comPagination,
+    // "com-select": comSelect
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.grid-content {
+  border: 1px solid #e8e8e8;
+  border-bottom: none;
+  .title {
+    background: #fafafa;
+    padding: 12px 10px;
+    border-bottom: 1px solid #ebeef5;
+    line-height: 20px;
+  }
+  .el-radio-group {
+    width: 100%;
+    .el-radio {
+      padding: 12px 10px;
+      border-bottom: 1px solid #ebeef5;
+      display: block;
+      margin-right: 0;
+      &:last-child {
+        // border-bottom: none;
+      }
+    }
+  }
+}
+.body-layer {
+  .body-but {
+    text-align: right;
+    margin-bottom: 15px;
+  }
+}
+.table-ctrl-right {
+  .search-scene {
+    margin: 0 20px 0 26px;
+  }
+  i {
+    margin-left: 20px;
+    font-size: 1.7rem;
+    vertical-align: middle;
+    cursor: pointer;
+
+    &.active {
+      color: var(--primaryColor);
+    }
+  }
+}
+
+.user-from {
+  // width: 380px;
+  margin: 0 auto;
+}
+
+.tip {
+  position: fixed;
+  left: 50%;
+  top: 50%;
+  z-index: 999;
+
+  &::before {
+    content: "密码重置为Fcb20210225,可登录修改";
+    position: absolute;
+    width: 205px;
+    left: 50%;
+    transform: translateX(-50%);
+    bottom: 100%;
+    border-radius: 4px;
+    padding: 10px;
+    z-index: 2000;
+    font-size: 12px;
+    line-height: 1.2;
+    min-width: 10px;
+    word-wrap: break-word;
+    background: #303133;
+    color: #fff;
+    margin-bottom: 6px;
+  }
+
+  &::after {
+    content: "";
+    position: absolute;
+    display: block;
+    width: 0;
+    height: 0;
+    bottom: 100%;
+    left: 50%;
+    transform: translateX(-50%);
+    border-color: transparent;
+    border-style: solid;
+    border-width: 6px;
+    border-top-color: #303133;
+    border-bottom-width: 0;
+  }
+}
+
+.maker {
+  font-weight: 400;
+  color: #969799;
+  line-height: 20px;
+}
+</style>
+
+<style lang="less">
+.user-table.el-table .cell {
+  overflow: inherit;
+}
+.el-tree {
+  max-height: 400px;
+  overflow-y: auto;
+  border-bottom: 1px solid #ebeef5;
+  .el-tree-node__content {
+    border-bottom: 1px solid #ebeef5;
+    padding: 12px 10px;
+    &:last-child {
+      // border-bottom: none;
+    }
+  }
+}
+</style>

+ 22 - 98
src/view/scene/Initiator.vue

@@ -1,99 +1,6 @@
 <template>
   <div class="scene-layer">
-    <iframe :src="url" ></iframe>
-    <div class="deteil-layer" :class="{hide: !showInfo}">
-      <div class="ctrl" @click="showInfo = !showInfo">
-        <img src="@/assets/image/decoration_collect@2x.png" alt="">
-      </div>
-      
-      <div class="deteil">
-
-
-        <h2>火调详情</h2>
-
-        <div class="block">
-          <h3>基本信息</h3>
-
-          <div class="base-info">
-            <div>
-              <span>项目编号:</span>
-              <p>{{detail.projectSn}}</p>
-            </div>
-            <div>
-              <span>起火地址:</span>
-              <p>{{detail.projectAddress}}</p>
-            </div>
-            <div>
-              <span>起火场所:</span>
-              <p>{{detail.projectSite}}</p>
-            </div>
-            <div>
-              <span>承办单位:</span>
-              <p>{{detail.organizerDeptName}}</p>
-            </div>
-            <div>
-              <span>起火对象:</span>
-              <p>{{detail.projectName}}</p>
-            </div>
-            <div>
-              <span>承办人员:</span>
-              <p>{{detail.organizerUsers}}</p>
-            </div>
-            <div>
-              <span>事故日期:</span>
-              <p>{{detail.accidentDate}}</p>
-            </div>
-            <div>
-              <span>火灾原因:</span>
-              <p>{{detail.fireReason}}</p>
-            </div>
-            <div>
-              <span>项目状态:</span>
-              <p>{{detail.status === 0 ? '未认定' : '已认定'}}</p>
-            </div>
-            <div>
-              <span>是否教学项目:</span>
-              <p>{{detail.isTeached ? '是' : '否'}}</p>
-            </div>
-            <div>
-              <span>创建人:</span>
-              <p>{{detail.creatorName}}</p>
-            </div>
-            <div>
-              <span>编辑人:</span>
-              <p>{{detail.editorName}}</p>
-            </div>
-            <div>
-              <span>创建时间:</span>
-              <p>{{detail.createTime}}</p>
-            </div>
-            <div>
-              <span>最新编辑时间:</span>
-              <p>{{detail.editTime}}</p>
-            </div>
-          </div>
-        </div>
-
-        <div class="block">
-          <h3>火灾档案</h3>
-
-          <div class="attach">
-            <div
-              v-for="item in tableData"
-              :key="item._title"
-              class="type-item">
-              <h4>{{ item.title }}</h4>
-              <div class="addpend">
-                <span v-for="item in item.children" :key="item.id" @click="goto(item)" >
-                  {{ item.fileName }}
-                  <img src="@/assets/image/goto.png" alt="">
-                </span>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
+    <iframe ref="lookLive" allow="geolocation; microphone; camera; midi; encrypted-media;" :src="url" ></iframe>
   </div>
 
   <com-dialog title="访问密码" enterText="确 定" v-model:show="confirm.show" @submit="loadData()" width="480" :showClose="false">
@@ -135,9 +42,14 @@ export default {
   },
   computed: {
     url() {
-        const {sceneNum,id} = this.$route.query
+      let ssurl = sessionStorage.getItem('initiator')
+      const {sceneNum,id} = this.$route.query
+      if(ssurl){
+        return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html' + ssurl.substr(ssurl.indexOf('?') || 0)
+      }
       if (sceneNum && id) {
-        return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html?m=' + sceneNum +'&p_id='+id
+        // return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html?m=' + m +'&p_id='+p_id
+        return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html?m=' + sceneNum +'&app=fire-control&p_id='+id+'&vlog#/'
         // return 'https://test.4dkankan.com/fire-rtc-live.html?m=t-3fp7zQl&p_id=PROJECT01456470052986503168#/?mode=2&name=test&role=leader&roomId=9c4Q2rbL9ZP0Pxf1ReXLekBN&userId=9RYJ5jgQYvnP'
       } else {
           console.log('url获取=>',this.$route)
@@ -183,16 +95,28 @@ export default {
         this.showImg = item.fileOssUrl
       }
     },
-    handleMessage(event){
+    async handleMessage(event){
+        if(event.data){
+            const {liveStatus,url} = event.data
+            url&&sessionStorage.setItem('initiator',url)
+            if(liveStatus == 0){//已结束
+                sessionStorage.removeItem('initiator')
+            }
+        }
         console.log('handleMessage===>',event)
     }
   },
   mounted() {
-    this.loadData()
+    // this.loadData()
     window.addEventListener('message',this.handleMessage)
+    let iframdata = this.$refs.lookLive
+    if(iframdata){
+        iframdata.allow = 'geolocation; microphone; camera; midi; encrypted-media;'
+    }
   },
   beforeDestroyed(){
     window.removeEventListener("message", this.handleMessage);
+    sessionStorage.removeItem('initiator')
   },
   components: {
     "com-dialog": comDialog,

+ 55 - 109
src/view/scene/visitor.vue

@@ -1,99 +1,6 @@
 <template>
   <div class="scene-layer">
-    <iframe :src="url" ></iframe>
-    <div class="deteil-layer" :class="{hide: !showInfo}">
-      <div class="ctrl" @click="showInfo = !showInfo">
-        <img src="@/assets/image/decoration_collect@2x.png" alt="">
-      </div>
-      
-      <div class="deteil">
-
-
-        <h2>火调详情</h2>
-
-        <div class="block">
-          <h3>基本信息</h3>
-
-          <div class="base-info">
-            <div>
-              <span>项目编号:</span>
-              <p>{{detail.projectSn}}</p>
-            </div>
-            <div>
-              <span>起火地址:</span>
-              <p>{{detail.projectAddress}}</p>
-            </div>
-            <div>
-              <span>起火场所:</span>
-              <p>{{detail.projectSite}}</p>
-            </div>
-            <div>
-              <span>承办单位:</span>
-              <p>{{detail.organizerDeptName}}</p>
-            </div>
-            <div>
-              <span>起火对象:</span>
-              <p>{{detail.projectName}}</p>
-            </div>
-            <div>
-              <span>承办人员:</span>
-              <p>{{detail.organizerUsers}}</p>
-            </div>
-            <div>
-              <span>事故日期:</span>
-              <p>{{detail.accidentDate}}</p>
-            </div>
-            <div>
-              <span>火灾原因:</span>
-              <p>{{detail.fireReason}}</p>
-            </div>
-            <div>
-              <span>项目状态:</span>
-              <p>{{detail.status === 0 ? '未认定' : '已认定'}}</p>
-            </div>
-            <div>
-              <span>是否教学项目:</span>
-              <p>{{detail.isTeached ? '是' : '否'}}</p>
-            </div>
-            <div>
-              <span>创建人:</span>
-              <p>{{detail.creatorName}}</p>
-            </div>
-            <div>
-              <span>编辑人:</span>
-              <p>{{detail.editorName}}</p>
-            </div>
-            <div>
-              <span>创建时间:</span>
-              <p>{{detail.createTime}}</p>
-            </div>
-            <div>
-              <span>最新编辑时间:</span>
-              <p>{{detail.editTime}}</p>
-            </div>
-          </div>
-        </div>
-
-        <div class="block">
-          <h3>火灾档案</h3>
-
-          <div class="attach">
-            <div
-              v-for="item in tableData"
-              :key="item._title"
-              class="type-item">
-              <h4>{{ item.title }}</h4>
-              <div class="addpend">
-                <span v-for="item in item.children" :key="item.id" @click="goto(item)" >
-                  {{ item.fileName }}
-                  <img src="@/assets/image/goto.png" alt="">
-                </span>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
+    <iframe ref="lookLive" allow="geolocation; microphone; camera; midi; encrypted-media;" :src="url" ></iframe>
   </div>
 
   <com-dialog title="访问密码" enterText="确 定" v-model:show="confirm.show" @submit="loadData()" width="480" :showClose="false">
@@ -107,8 +14,8 @@
 </template>
 
 <script>
-import { fireDetailByPsw, getAttachListByPsw } from '@/request/config'
-import axios from 'axios';
+// import { fireDetailByPsw, getAttachListByPsw } from '@/request/config'
+// import axios from 'axios';
 import comDialog from "@/components/dialog";
 import { types } from '@/constant'
 
@@ -119,7 +26,7 @@ export default {
 
       },
       showImg: '',
-      confirm: {show: true, psw: ''},
+      confirm: {show: false, psw: ''},
       list: [],
       showInfo: false,
       loadSuccess: false,
@@ -135,12 +42,18 @@ export default {
   },
   computed: {
     url() {
-        const {sceneNum,id} = this.$route.params
-      if (sceneNum && id) {
-        return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html?m=' + sceneNum +'&p_id='+id
-        // return 'https://test.4dkankan.com/fire-rtc-live.html?m=t-3fp7zQl&p_id=PROJECT01456470052986503168#/?mode=2&name=test&role=leader&roomId=9c4Q2rbL9ZP0Pxf1ReXLekBN&userId=9RYJ5jgQYvnP'
+      let ssurl =  window.location.hash.substr(window.location.hash.indexOf('?') || 0)
+      let dataurl = sessionStorage.getItem('visitorurl')
+    //   console.log('dataurl',dataurl,dataurl.substr(dataurl.indexOf('?') || 0))
+      if(dataurl){
+        return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html' + dataurl.substr(dataurl.indexOf('?') || 0)
+      }
+    //   const {m,p_id} = this.$route.query
+      if (ssurl) {
+        // return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html?m=' + sceneNum +'&p_id='+id
+        return process.env.VUE_APP_DOMAIN + '/fire-rtc-live.html' + ssurl
       } else {
-          console.log('url获取=>',this.$route.params)
+          console.log('url获取=>',this.$route)
         return 'javascript:void(0)'
         // return process.env.VUE_APP_DOMAIN + '/spc.html?m=' + this.detail.sceneNum
       }
@@ -166,15 +79,16 @@ export default {
   methods: {
     async loadData() {
       // if (!this.confirm.psw || this.confirm.psw.length === 0) return this.$message.error('请输入访问密码', '提示')
-      let {data: detail} = await axios.get(fireDetailByPsw, {params: {projectId: this.$route.params.projectId, randCode: this.confirm.psw,type:1}})
-      this.detail = detail
+    //   let {data: detail} = await axios.get(fireDetailByPsw, {params: {projectId: this.$route.params.projectId, randCode: this.confirm.psw,type:1}})
+    //   this.detail = detail
+
+    //   let {data: list} = await axios.get(getAttachListByPsw, {params: {projectId: this.$route.params.projectId, randCode: this.confirm.psw,type:1}})
+    //   this.list = list
 
-      let {data: list} = await axios.get(getAttachListByPsw, {params: {projectId: this.$route.params.projectId, randCode: this.confirm.psw,type:1}})
-      this.list = list
+    //   this.loadSuccess = true
+    //   this.confirm.show = false
+    //   this.showInfo = false  //不默认开启
 
-      this.loadSuccess = true
-      this.confirm.show = false
-      this.showInfo = false  //不默认开启
     },
     goto(item) {
       if (item.type !== 1) {
@@ -182,10 +96,42 @@ export default {
       } else {
         this.showImg = item.fileOssUrl
       }
+    },
+    async handleMessage(event){
+        var that = this
+        if(event.data){
+            const {liveStatus,url} = event.data
+            url&&sessionStorage.setItem('visitorurl',url)
+            if(liveStatus == 0){//已结束
+                sessionStorage.removeItem('visitorurl')
+                let isOk = await this.$alert(
+                    "带看已结束",
+                    "确定"
+                    ,{
+                        showClose:false,
+                        showCancelButton:false,
+                        closeOnClickModal:false,
+                    }
+                );
+                if(isOk){
+                    that.$router.push({name: 'home'})
+                }
+            }
+        console.log('handleMessage===>',event.data)
+        }
     }
   },
   mounted() {
     this.loadData()
+    window.addEventListener('message',this.handleMessage)    
+    let iframdata = this.$refs.lookLive
+    if(iframdata){
+        iframdata.allow = 'geolocation; microphone; camera; midi; encrypted-media;'
+    }
+  },
+  beforeDestroyed(){
+    window.removeEventListener("message", this.handleMessage);
+    sessionStorage.removeItem('visitorurl')
   },
   components: {
     "com-dialog": comDialog,

+ 2 - 2
src/view/user/index.vue

@@ -103,7 +103,7 @@
           <el-input
             v-model.trim="editName"
             placeholder="请输入"
-            maxlength="15"
+            maxlength="30"
           ></el-input>
         </el-form-item>
       </el-form>
@@ -119,7 +119,7 @@
       <el-form ref="form" :model="form" label-width="90px" class="user-from">
         <el-form-item label="用户姓名" class="mandatory" >
           <el-input
-            maxlength="15"
+            maxlength="30"
             v-model="newData.nickName"
             placeholder="请输入"
           ></el-input>

+ 158 - 10
src/view/vrmodel/index.vue

@@ -43,12 +43,19 @@
       <el-table-column label="场景标题" prop="sceneName"></el-table-column>
       <el-table-column label="S/N码" prop="snCode"></el-table-column>
       <el-table-column label="浏览数量" prop="viewCount"></el-table-column>
-      <el-table-column label="拍摄时间" prop="createTime" v-slot:default="{ row }"> {{row.createTime.substr(0, 11)}}</el-table-column>
-      <el-table-column label="操作" v-slot:default="{ row }" >
-        <span class="oper-span" @click="shareHandle(row)" v-power="'view'">查看</span>
-        <span class="oper-span" v-if="(user.roleKey=='admin-ordinary' && user.info.id == row.creatorId) ||user.roleKey=='admin-dept'" @click="auth.update && user.info.id == row.creatorId && editModel(row)"   v-power="'edit'">编辑</span> 
-        <!-- v-if="auth.update || (user.roleKey=='admin-ordinary' && user.info.id == row.creatorId) " -->
-        <span class="oper-span" v-if="(user.roleKey=='admin-ordinary' && user.info.id == row.creatorId) ||user.roleKey=='admin-dept'" @click="auth.delete && user.info.deptId == row.deptId && dataList.delete(row)" v-power="'del'" style="color: var(--primaryColor)">删除</span>
+      <el-table-column
+        label="拍摄时间"
+        prop="createTime"
+        v-slot:default="{ row }"
+      >
+        {{ row.createTime.substr(0, 11) }}</el-table-column
+      >
+      <el-table-column label="操作" v-slot:default="{ row }">
+        <span class="oper-span" v-power="'view'" @click="shareHandle(row)">查看</span>
+        <span class="oper-span" v-power="'edit'" @click="editModel(row)">编辑</span>
+        <span class="oper-span" @click="download(row)" v-if="row.num" >下载</span>
+        <span class="oper-span" v-power="'del'" @click="dataList.delete(row)" style="color: var(--primaryColor)"  >删除</span
+        >
       </el-table-column>
     </el-table>
 
@@ -95,7 +102,49 @@
       </el-form>
     </com-dialog>
 
-    
+    <com-dialog
+      title="场景离线包下载"
+      :width="500"
+      v-model:show="cameraDownload.show"
+      :hideFloor="cameraDownload.data.type == '1'"
+      enterText="下 载"
+      @quit="suspend"
+      @submit="downloadItemCompany"
+    >
+      <div>
+        <div class="title">
+          {{
+            cameraDownload.data.type == "0"
+              ? "下载场景离线数据包,可在本地运行查看。"
+              : cameraDownload.data.type == "1"
+              ? "正在打包场景离线数据"
+              : cameraDownload.data.name
+          }}
+        </div>
+        <div style="marginTop:20px" v-if="cameraDownload.data.type == '0'">
+          当前剩余下载次数:<span>{{ cameraDownload.data.value }}</span
+          >次
+        </div>
+        <div v-else-if="cameraDownload.data.type == '1'">
+          <div
+            class="text"
+            style="display:flex;justify-content: space-between;margin-top:15px"
+          >
+            <span>{{ cameraDownload.data.name }}</span>
+            <span>{{ cameraDownload.data.percent }}%</span>
+          </div>
+          <div>
+            <el-slider
+              :disabled="true"
+              v-model="cameraDownload.data.percent"
+              :show-tooltip="false"
+            >
+            </el-slider>
+          </div>
+        </div>
+      </div>
+    </com-dialog>
+
     <com-dialog
       title="同步场景"
       v-model:show="asyncSceneCompany"
@@ -119,10 +168,15 @@ import comPagination from "@/components/pagination";
 import comCompany from "@/components/company-select";
 import { getApp } from '@/app'
 import { dateFormat } from '@/util'
+import { ADMIN_USER_ID } from '@/constant'
 import {
   getSceneList,
   deleteScene,
-  getCameraOptions,
+  // getCameraOptions,
+  getCameraListByUser,
+  checkHasDownload,
+  getDownloadProcess,
+  downloadScene,
 } from '@/request/config'
 import axios from 'axios';
 
@@ -143,6 +197,7 @@ export default {
     const headList = ref([{ name: "场景管理", value: 2 }]);
     const currModel = ref(1)
     const time = ref(null)
+    const cameraDownload = ref({ data: { type: 0, value: 0 }, show: false });
 
     watch(time, () => {
       if (time.value) {
@@ -172,11 +227,42 @@ export default {
       asyncSceneCompany.value || state.dataList.value.refer()
     })
 
-    return { ...state, headList, currModel, time, auth, editCompany, cameras, cameraCompany, asyncSceneCompany, user };
+    return { ...state, headList, currModel, time, auth, editCompany, cameras, cameraCompany, asyncSceneCompany, user,cameraDownload,ADMIN_USER_ID };
   },
   methods: {
+    download(item) {
+      //查询下载进度   0未创建  1打包中  2等待下载
+      let statelist = {
+        0:0,
+        1:1,
+        2:0,
+        3:2,
+      }
+      axios
+        .get(checkHasDownload + item.num, { sceneNum: item.num, isTiles: true })
+        .then((res) => {
+          console.log("type---> res :", res.data);
+          this.cameraDownload.data = {
+            type: statelist[res.data.downloadStatus],
+            downloadStatus:res.data.downloadStatus,
+            sceneNum: item.num,
+            value: res.data.count,
+            name: item.sceneName + ".zip",
+            url: res.data.downloadUrl,
+            percent:0,
+          };
+          if(res.data.count == 0 && (res.data.downloadStatus == 0 || res.data.downloadStatus == 2)){
+            return this.$message.error("暂无剩余下载次数!", "提示");
+          }else{
+          this.cameraDownload.show = true;
+          }
+          if(res.data.downloadStatus == 1){//下载中
+            this.downloadItemCompany()
+          }
+        });
+    },
     async activated() {
-      let res = await axios.get(getCameraOptions)
+      let res = await axios.get(getCameraListByUser)
       this.cameras = res.data.list
     },
     editModel(item) {
@@ -185,6 +271,65 @@ export default {
     shareHandle(item) {
       window.open(process.env.VUE_APP_DOMAIN + '/spc.html?m=' + item.num)
     },
+    suspend(){
+      clearTimeout(this.timer)
+    },
+    downloadItemCompany() {
+      //查询下载进度   0未创建  1打包中  2等待下载
+      //查询进度
+      let that = this
+      const { sceneNum, url,downloadStatus } = this.cameraDownload.data;
+      let download = (downloadUrl) =>{
+        let iframe = document.createElement('iframe')
+        iframe.style.display = 'none'
+        iframe.src = downloadUrl
+        document.body.appendChild(iframe);
+        that.cameraDownload.show = false;
+      }
+      console.log('downloadStatus',downloadStatus)
+      if(downloadStatus == 3){
+        return download(url)
+      }else if(downloadStatus == 0 || downloadStatus == 2 ){
+        axios.get(downloadScene+sceneNum).then((resdata) => {
+          if(resdata.data.downloadStatus !== 1){
+            clearTimeout(this.timer)
+            that.cameraDownload.show = false;
+            return this.$message.error("暂无剩余下载次数!", "提示");
+          }
+        });
+      }
+      let fn = () => {
+        that.timer = setTimeout(() => {
+          callback();
+        }, 1000);
+        // downIngs[this.scene.num] = this.timer;
+      };
+      let callback = () => {
+      axios.get(getDownloadProcess, {
+        params:{
+          sceneNum: sceneNum,
+          isTiles: true,
+        }
+        }).then((res) => {
+          const { percent,url } = res.data
+          that.cameraDownload.data.type  = 1
+          that.cameraDownload.data.percent  = parseInt(percent)
+          if(!url){
+            fn()
+          }else{ //拿到下载链接
+            url&&download(url)
+          }
+        });
+      }
+      fn()
+      // if (type !== 2) {
+      //   this.cameraDownload.data = {
+      //     ...cameraDownloaddata,
+      //     type: 1,
+      //     value: 50,
+      //   };
+      // }
+    },
     updateCameraCompany() {
       if (!this.cameraCompany.data) {
         return this.$message.error('请选择相机!', '提示')
@@ -281,4 +426,7 @@ export default {
 .vrmodel-from .el-select {
   width: 100%;
 }
+.el-slider__button-wrapper {
+  top: -6px !important;
+}
 </style>

+ 4 - 2
vue.config.js

@@ -14,15 +14,17 @@ module.exports = {
     inline: false,
     hot: false,
     liveReload: false,
+    // https:true,
+    // port:8080,
     // 设置代理proxy
     proxy: {
-      '/fireApi': {
+      '/__api': {
         // target: 'http://192.168.0.26:8585/',
         target: 'https://testxfhd.4dkankan.com',
         // target: 'https://testhuodiao.4dkankan.com/',
         changeOrigin: true,  
         pathRewrite: {      
-          ['^/fireApi']: ''
+          ['^/__api']: ''
         }
       }
     }