tremble 5 yıl önce
ebeveyn
işleme
e0fe19b8a0

+ 10 - 6
mobile/src/assets/font/iconfont.css

@@ -1,11 +1,11 @@
 @font-face {
   font-family: 'iconfont';  /* project id 941679 */
-  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot');
-  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot?#iefix') format('embedded-opentype'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.woff2') format('woff2'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.woff') format('woff'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.ttf') format('truetype'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.svg#iconfont') format('svg');
+  src: url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.eot');
+  src: url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.woff') format('woff'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.svg#iconfont') format('svg');
 }
 
 .iconfont {
@@ -16,6 +16,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-case_teamwork:before {
+  content: "\e709";
+}
+
 .icon-teaching:before {
   content: "\e6bb";
 }

+ 171 - 0
mobile/src/components/toast/cooperation.vue

@@ -0,0 +1,171 @@
+<template>
+  <div
+    class="toast-layout"
+    :style="{background:false?'none':'rgba(0, 0, 0, 0.3)'}"
+    :class="{'toast-active':visible}"
+  >
+    <div class="toast-con bind-con" :style="{minWidth:'320px'}">
+      <div class="t-header ">
+        <span>{{lang==='en'?'Invite collaborator':'分配协助'}}</span>
+        <i class="iconfont icon-cuowu" @click="handleClick"></i>
+      </div>
+      <div class="binding-con">
+        <div class="binding-body">
+          <div class="toclient">{{lang==='en'?'Add collaborator':'分配场景给用户'}}</div>
+          <div class="b-input" >
+            <input v-model="userName" :placeholder="lang==='en'?'User name':'请输入用户账号'" type="text">
+          </div>
+        </div>
+      </div>
+
+      <div class="bind-btn coo-btn" >
+        <span @click="addCooperation">{{lang==='en'?'OK':'确定'}}</span>
+        <span class="default" @click="handleClick">{{lang==='en'?'Cancel':'取消'}}</span>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<script>
+import toastZH from '@/store/language/cn/toast'
+import toastEN from '@/store/language/en/toast'
+
+export default {
+  props: ['sceneNum', 'visible'],
+  data () {
+    return {
+      userName: '',
+      lang: localStorage.getItem('language'),
+      toastCode: localStorage.getItem('language') === 'en' ? toastEN : toastZH
+
+    }
+  },
+  computed: {
+
+  },
+  watch: {
+    visible: function (newVal) {
+      this.userName = ''
+      this.lang = localStorage.getItem('language')
+      this.toastCode = this.lang === 'en' ? toastEN : toastZH
+    }
+  },
+  methods: {
+    handleClick () {
+      this.$emit('closePoint')
+    },
+    async addCooperation () {
+      let token = localStorage.getItem('token')
+      if (!this.userName) {
+        return this.$toast.show('error', `${this.lang === 'en' ? 'Please enter user name.' : '请输入用户名'}`)
+      }
+      let result = await this.$http({
+        method: 'post',
+        data: {
+          userName: this.userName,
+          sceneNum: this.sceneNum
+        },
+        headers: {
+          token
+        },
+        url: '/user/scene/cooperation/save'
+      })
+      let data = result.data
+      if (data.code === 0) {
+        this.handleClick()
+        this.$toast.show('success', this.toastCode['50'])
+      } else {
+        return this.$toast.show('warn', this.toastCode[data.code], () => {})
+      }
+    }
+  }
+}
+
+</script>
+
+<style lang="scss" scoped>
+@import "./style.scss";
+$theme-color: #1fe4dc;
+
+.bind-con{
+  .t-header{
+    height: 50px;
+    line-height: 50px;
+    text-align: center;
+    .iconfont{
+      position: absolute;
+      right: 10px;
+    }
+  }
+  .binding-con{
+    text-align: center;
+    .binding-body{
+      .toclient{
+        color: #202020;
+        margin-bottom: 14px;
+        font-size: 14px;
+      }
+      .b-input{
+        width: 70%;
+        margin: 0 auto 14px;
+        border: solid 1px #e7e7e7;
+        input{
+          width: 100%;
+          text-align: center;
+          color: #969696;
+          height: 30px;
+          padding: 0 20px 0 10px;
+          line-height: 30px;
+          font-size: 12px;
+          border: none;
+        }
+      }
+      .notbing{
+        border: 1px solid #ff0000;
+      }
+      .bind-info{
+        width: 90%;
+        margin: 15px auto;
+        color: #2d2d2d;
+        text-align: left;
+        p{
+          line-height: 1.5;
+        }
+      }
+      .bind-error{
+        color: #ff0000;
+        font-size: 12px;
+        text-align: center;
+        margin-top: 5px;
+      }
+    }
+  }
+
+  .coo-btn{
+    text-align: right;
+    >span{
+      text-align: center;
+    }
+    .default{
+      background-color: #E6E6E6;
+    }
+  }
+
+  .bind-btn{
+    text-align: center;
+    background: #f7f7f7;
+    span{
+      display: inline-block;
+      margin:  0 auto;
+      line-height: 30px;
+      font-size: 14px;
+      padding: 0 10px;
+      margin: 10px 0;
+      background-color: $theme-color;
+      color: #2d2d2d;
+      border-radius: 4px;
+    }
+  }
+}
+</style>

+ 5 - 1
mobile/src/components/toast/index.vue

@@ -2,6 +2,7 @@
   <div>
     <binding :btype="bindingType" :visible='bindingVisible' @closePoint="()=>{bindingVisible = false,emitCallback()}"/>
     <addcart :visible='addcartVisible' :img='img' :msg='addcartmsg' :txt='addcarbtnTxt' @closePoint="handleAddCart" />
+    <cooperation :sceneNum="sceneNum" :visible='cooperationVisible' @closePoint="()=>{cooperationVisible = false,emitCallback()}"/>
 
     <div
       class="toast-layout"
@@ -44,6 +45,7 @@
 <script>
 import binding from './binding'
 import addcart from './addcart'
+import cooperation from './cooperation'
 
 let types = {
   warn: '提示',
@@ -58,14 +60,16 @@ let typesEn = {
 }
 
 export default {
-  components: {binding, addcart},
+  components: {binding, addcart, cooperation},
   data () {
     return {
       bindingType: 1,
+      sceneNum: '',
       visible: false,
       bindingVisible: false,
       addcartVisible: false,
       addcartmsg: false,
+      cooperationVisible: false,
       addcarbtnTxt: false,
       message: '',
       submsg: '',

+ 6 - 0
mobile/src/components/toast/toast.js

@@ -51,6 +51,12 @@ Toast.install = function (Vue) {
       instance.callback = callback || function () {
       }
     },
+    showCooperation: (sceneNum, callback) => {
+      instance.sceneNum = sceneNum
+      instance.cooperationVisible = true
+      instance.callback = callback || function () {
+      }
+    },
     show: (type, msg, callback, submsg = '') => {
       instance.img = imgs[type] || (cdn + 'images/icon/success.png')
       instance.submsg = submsg

+ 56 - 6
mobile/src/pages/account/manage/index.vue

@@ -8,8 +8,16 @@
       <span v-if="Number(consumpselected.id)===2&&(title==='消费记录'||title==='Billing Records')" @click="openInvoice" class="btns">
         开具发票
       </span>
-      <div v-if="title==='我的场景'||title==='My Scene'">
-        <i class="iconfont icon-sousuo" @click="$router.push({name:'scenesearch'})"></i>
+
+      <div v-if="title==='场景管理'||title==='Scene Management'" style="min-width:12%;" class="select" ref="mbMenu1" @click="selectedActive=!selectedActive">
+        {{sceneSelected.name}}
+        <div class="s-sub" :class="{'s-active':selectedActive,'s-suben':language==='en'}">
+          <div v-for="(item,i) in sceneType" :key="i" :class="{'item-active':sceneSelected.name===item.name}" @click="emithandle(item)" class="s-item">{{item.name}}</div>
+        </div>
+      </div>
+
+      <div v-if="title==='场景管理'||title==='Scene Management'">
+        <i class="iconfont icon-sousuo" @click="$router.push({name:'scenesearch',params:{id:$route.params.id}})"></i>
       </div>
       <div v-if="title==='我的相机'||title==='My Cameras'" style="min-width:12%;" class="select" ref="mbMenu1" @click="selectedActive=!selectedActive">
         {{selected.name}}
@@ -34,7 +42,7 @@ import {mapState} from 'vuex'
 let titleName = {
   'information': '个人中心',
   'order': '我的订单',
-  'myscene': '我的场景',
+  'myscene': '场景管理',
   'device': '我的相机',
   'consumption': '消费记录',
   'consumpdetail': '记录详细',
@@ -51,7 +59,7 @@ let titleName = {
 let titleNameEn = {
   'information': 'Account Information',
   'order': 'My Order',
-  'myscene': 'My Scene',
+  'myscene': 'Scene Management',
   'device': 'My Cameras',
   'consumption': 'Billing Records',
   'consumpdetail': 'Detail',
@@ -102,6 +110,37 @@ let consumpTypeEn = [
     id: 0
   }
 ]
+
+let sceneid = {
+  'my': '我的场景',
+  'co': '协作场景'
+}
+
+let sceneidEn = {
+  'my': 'My Scene',
+  'co': 'Collaborative Scene'
+}
+
+let sceneType = [
+  {
+    name: '我的场景',
+    id: 'my'
+  }, {
+    name: '协作场景',
+    id: 'co'
+  }
+]
+
+let sceneTypeEn = [
+  {
+    name: 'My Scene',
+    id: 'my'
+  }, {
+    name: 'Collaborative Scene',
+    id: 'co'
+  }
+]
+
 export default {
   watch: {
     language (newVal) {
@@ -134,6 +173,10 @@ export default {
     consumpType: function () {
       return this.language === 'en' ? consumpTypeEn : consumpType
     },
+
+    sceneType: function () {
+      return this.language === 'en' ? sceneTypeEn : sceneType
+    },
     noNavs: function () {
       for (let i = 0; i < noNav.length; i++) {
         let ele = noNav[i]
@@ -148,6 +191,14 @@ export default {
         name: deviceid[this.$route.params.id],
         id: this.$route.params.id
       }
+    },
+    sceneSelected: function () {
+      let sceneids = this.language === 'en' ? sceneidEn : sceneid
+
+      return {
+        name: sceneids[this.$route.params.id],
+        id: this.$route.params.id
+      }
     }
   },
   methods: {
@@ -163,13 +214,12 @@ export default {
     },
     emithandle (item) {
       let name = this.$route.name
-      console.log(name)
       switch (name) {
         case 'device':
           this.$router.push({name: 'device', params: {id: item.id}})
           break
         default:
-          this.$router.push({name: 'myscene'})
+          this.$router.push({name: 'myscene', params: {id: item.id}})
           break
       }
     },

+ 71 - 19
mobile/src/pages/account/manage/myscene/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="scene-layout">
+  <div class="scene-layout" ref="ulMenu">
     <template v-if="total">
       <div
         class="items"
@@ -12,6 +12,8 @@
           :style="{backgroundImage: `url(${getSceneImg(item)})`}"
         >
           <img v-if="((item.status === 1||item.status===-2)&&item.payStatus !== -2)" @click.stop="handleShare(item)" :src="`${$cdn}images/share-btn.png`" alt />
+          <div class="cooperation" v-if="item.cooperationUserName"><i class="iconfont icon-case_teamwork"></i><span>{{item.cooperationUserName}}</span></div>
+
           <div class="loading-hover" v-if="item.status === 0">
             <div class="loading-icon">
               <span class="refreshing-loader"></span>
@@ -31,22 +33,27 @@
           <p>{{langScenes.shooting}}: {{item.createTime}}</p>
           <p>S/N: {{item.snCode||item.childName}}</p>
           <div class="btm-opr">
-            <!-- <div class="btm-left">
-              <img :src="item.sceneScheme>=4?`${$cdn}images/eye-pro.png`:`${$cdn}images/eye_lite.png`" alt>
-              <span>{{item.sceneScheme>=4?'Pro':'Lite'}}</span>
-            </div>-->
-            <div class="btmr-con">
-              <div
-              v-if="item.status !== 0"
-                class="btm-right"
-                @click.stop="del(item)"
-              ><span>{{langScenes.delete}}</span></div>
-              <div
-              v-if="item.status === 1||item.status===-2"
-                class="btm-right primary"
-                @click.stop="gotoEdit(item)"
-              ><span>{{langScenes.edit}}</span></div>
-            </div>
+            <template v-if="$route.params.id==='my'">
+              <div @click.stop="handleMenu(i)">
+                  <span class="spot"></span>
+                </div>
+                <ul v-if="ulActive === i">
+                  <li v-if="item.status === 1||item.status===-2" @click.stop="gotoEdit(item)">{{langScenes.edit}}</li>
+                  <li v-if="item.status === 1||item.status===-2" @click.stop="handleCooperation(item)">{{item.cooperationUserId?langScenes.qxfp:langScenes.fenpei}}</li>
+                  <li v-if="item.status !== 0" @click.stop="del(item)">{{langScenes.delete}}</li>
+                </ul>
+            </template>
+
+            <template v-else>
+              <div class="btmr-con">
+                <div
+                v-if="item.status === 1||item.status===-2"
+                  class="btm-right"
+                  @click.stop="gotoEdit(item)"
+                ><span>{{langScenes.edit}}</span></div>
+              </div>
+            </template>
+
           </div>
         </div>
       </div>
@@ -105,7 +112,9 @@ export default {
       imgs,
       url: 'https://www.4dkankan.com/showProPC.html?m=KcMeJlOr8',
       total: 0,
-      item: ''
+      item: '',
+      ulActive: '',
+      tabActive: this.$route.params.id
     }
   },
   watch: {
@@ -114,6 +123,9 @@ export default {
     },
     active (newVal, oldVal) {
       this.currentPage === 1 ? this.getList() : this.currentPage = 1
+    },
+    '$route.params.id': function (newVal) {
+      this.currentPage === 1 ? this.getList() : this.currentPage = 1
     }
   },
   computed: {
@@ -136,9 +148,45 @@ export default {
     })
   },
   mounted () {
+    document.addEventListener('click', (e) => {
+      if (this.$refs.ulMenu) {
+        if (!this.$refs.ulMenu.contains(e.target)) {
+          this.ulActive = ''
+        }
+      }
+    })
     this.getList()
   },
   methods: {
+    async handleCooperation (item) {
+      if (item.cooperationUserId) {
+        let result = await this.$http({
+          method: 'post',
+          data: {
+            sceneNum: item.num
+          },
+          headers: {
+            token: this.token
+          },
+          url: '/user/scene/cooperation/delete'
+        })
+        let data = result.data
+        if (data.code === 0) {
+          this.$toast.show('success', this.langToast['51'], () => {
+            this.getList()
+          })
+        } else {
+          return this.$toast.show('warn', this.langToast[data.code], () => {})
+        }
+      } else {
+        this.$toast.showCooperation(item.num, () => {
+          this.getList()
+        })
+      }
+    },
+    handleMenu (index) {
+      this.ulActive = index === this.ulActive ? '' : index
+    },
     rechargeTip (item) {
       this.$toast.showConfirm('warn', this.langToast['28'], () => {
         this.$router.push({name: 'introduce', params: {id: item.childName}})
@@ -287,7 +335,11 @@ export default {
         // cameraId,
         // cameraType
       }
-      await this.$store.dispatch('getUserScene', params)
+      if (this.$route.params.id === 'co') {
+        await this.$store.dispatch('getCooperationScene', params)
+      } else {
+        await this.$store.dispatch('getUserScene', params)
+      }
       this.pageSize = this.myscene.pageSize
       this.total = this.myscene.total || 0
     }

+ 56 - 6
mobile/src/pages/account/manage/myscene/style.scss

@@ -52,6 +52,20 @@
         right: 10px;
         top: 10px;
       }
+      .cooperation{
+        background:rgba(0,0,0,0.5);
+        position: absolute;
+        bottom: 0;
+        width: 100%;
+        height: 26px;
+        text-align: center;
+        line-height: 26px;
+        color: #FFFFFF;
+        >span{
+          display: inline-block;
+          margin-left: 6px;
+        }
+      }
     }
     .item-txt{
       display: flex;
@@ -65,13 +79,49 @@
         justify-content: space-between;
         align-items: center;
         margin-top: 6px;
-        .btm-left{
-          vertical-align: middle;
-          img{
-            width: 30%;
+        position: relative;
+        .spot{
+          width: 4px;
+          height: 4px;
+          display: inline-block;
+          background-color: #202020;
+          border-radius: 50%;
+          position: relative;
+          margin-left: 10px;
+          &::after,&::before{
+            content: '';
+            position: absolute;
+            width: 4px;
+            height: 4px;
+            display: inline-block;
+            background-color: #202020;
+            border-radius: 50%;
+            left: -8px;
+            top: 50%;
+            transform: translateY(-50%);
+          }
+          &::before{
+            right: -8px;
+            left: unset;
           }
-          span{
-            color: #2d2d2d;
+        }
+        >ul{
+          position: absolute;
+          z-index: 9;
+          left: -32px;
+          top: 22px;
+          background: #F7F7F7;
+          min-width: 90px;
+          box-shadow:0px 1px 6px rgba(0,0,0,0.16);
+          >li{
+            text-align: left;
+            width: 100%;
+            line-height: 2.5;
+            padding: 0 10px;
+            color: #202020;
+            &:hover{
+              background-color: #EBEBEB;
+            }
           }
         }
         .btmr-con{

+ 5 - 0
mobile/src/pages/account/manage/style.scss

@@ -57,6 +57,11 @@ $theme-color: #1fe4dc;
           background: #f2f2f2;
         }
       }
+
+      .s-suben{
+        width: 145px;
+        left: -20%;
+      }
       .s-active{
         max-height: 100px;
       }

+ 2 - 2
mobile/src/router/index.js

@@ -41,7 +41,7 @@ let router = new Router({
       component: resolve => require(['@/pages/eight'], resolve)
     },
     {
-      path: '/scenesearch',
+      path: '/scenesearch/:id',
       name: 'scenesearch',
       component: resolve => require(['@/pages/account/manage/scenesearch'], resolve)
     },
@@ -147,7 +147,7 @@ let router = new Router({
         },
         {
           name: 'myscene',
-          path: '/myscene',
+          path: '/myscene/:id',
           component: resolve => require(['@/pages/account/manage/myscene'], resolve),
           meta: {requireAuth: true}
         },

+ 2 - 0
mobile/src/store/language/cn/manage.js

@@ -123,6 +123,8 @@ export default{
   myScenes: {
     edit: '编辑',
     delete: '删除',
+    fenpei: '分配协作',
+    qxfp: '取消协作',
     shooting: '拍摄时间:',
     id: '设备ID:',
     noScenes: '暂无任何场景,赶紧去拍摄吧。',

+ 5 - 0
mobile/src/store/language/cn/toast.js

@@ -50,6 +50,8 @@ export default{
   '47': '修改成功',
   '48': '暂不支持降级套餐',
   '49': '您可进行续费或升级套餐',
+  '50': '分配成功',
+  '51': '已取消协作',
 
   '4007': '用户名不存在',
   '4010': '绑定的相机不存在',
@@ -83,6 +85,9 @@ export default{
   '3017': '空文件',
   '3018': '需要上传或使用的文件不存在',
   '3019': '邮箱格式不正确',
+  '3021': '账号不存在,请核对后重新输入',
+  '3022': '该场景已添加协作者,请先取消协作后再添加',
+
   '8001': '订单不存在',
   '8002': '支付失败',
   '5001': 'modeldata.json为空',

+ 2 - 0
mobile/src/store/language/en/manage.js

@@ -124,6 +124,8 @@ export default{
   myScenes: {
     edit: 'Edit',
     delete: 'Delete',
+    fenpei: 'Invite collaborator',
+    qxfp: 'Cancel collaborator',
     placeholder: {
       searchID: 'Search scene'
     },

+ 5 - 0
mobile/src/store/language/en/toast.js

@@ -49,6 +49,8 @@ export default{
   '47': 'Modify successfully',
   '48': 'Downgrading process is currently not supported',
   '49': 'You can renew or upgrade the package.',
+  '50': 'Collaborator added!',
+  '51': 'Collaborator canceled.',
 
   '4007': 'User name doesn\'t exist.',
   '4010': 'The bound camera doesn\'t exist.',
@@ -82,6 +84,9 @@ export default{
   '3017': 'Empty file',
   '3018': 'The file to upload or to use doesn\'t exist.',
   '3019': 'Incorrect format of Email address.',
+  '3021': 'Invalid user name, please check and give a valid one.',
+  '3022': 'There is already a collaborator for this scene. Please cancel the previous collaboration first.',
+
   '8001': 'Order doesn\'t exist.',
   '8002': 'Payment failure',
   '5001': 'Empty modeldata.json',

+ 2 - 2
mobile/src/store/language/home_cn.js

@@ -138,8 +138,8 @@ export default {
         to: {path: '/information'}
       },
       {
-        name: '我的场景',
-        to: {name: 'myscene', params: {id: 4}}
+        name: '场景管理',
+        to: {name: 'myscene', params: {id: 'my'}}
       },
       {
         name: '我的相机',

+ 2 - 2
mobile/src/store/language/home_en.js

@@ -138,8 +138,8 @@ export default {
         to: {path: '/information'}
       },
       {
-        name: 'My Scene',
-        to: {name: 'myscene', params: {id: 4}}
+        name: 'Scene Management',
+        to: {name: 'myscene', params: {id: 'my'}}
       },
       {
         name: 'My Cameras',

+ 15 - 0
mobile/src/store/user.js

@@ -296,6 +296,21 @@ export default {
       context.commit('INVOICE', {type: data.type, data: info.data.data})
     },
 
+    async getCooperationScene (context, params) {
+      let res = await http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: context.state.token
+        },
+        url: '/user/scene/cooperation/cooperationSceneList'
+      })
+
+      let data = res.data
+      if (data.code !== 0) return
+      context.commit('myScene', data.data)
+    },
+
     async getUserScene (context, params) {
       let res = await http({
         method: 'post',

+ 11 - 6
pc/src/assets/font/iconfont.css

@@ -1,11 +1,11 @@
 @font-face {
   font-family: 'iconfont';  /* project id 941679 */
-  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot');
-  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot?#iefix') format('embedded-opentype'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.woff2') format('woff2'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.woff') format('woff'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.ttf') format('truetype'),
-  url('//at.alicdn.com/t/font_941679_any402rn3j.svg#iconfont') format('svg');
+  src: url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.eot');
+  src: url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.woff') format('woff'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_941679_i0oxeww8kbo.svg#iconfont') format('svg');
 }
 
 .iconfont {
@@ -16,6 +16,11 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-case_teamwork:before {
+  content: "\e709";
+}
+
+
 .icon-teaching:before {
   content: "\e6bb";
 }

+ 16 - 8
pc/src/components/toast/cooperation.vue

@@ -6,21 +6,21 @@
   >
     <div class="toast-con bind-con" :style="{minWidth:'505px'}">
       <div class="t-header ">
-        <span>{{lang==='en'?'分配协助':'分配协助'}}</span>
+        <span>{{lang==='en'?'Invite collaborator':'分配协作'}}</span>
         <i class="iconfont icon-cuowu" @click="handleClick"></i>
       </div>
       <div class="binding-con" style="height:150px">
         <div class="binding-body" style="width:450px;top:25px">
-          <div class="toclient">分配场景给用户</div>
+          <div class="toclient">{{lang==='en'?'Add collaborator':'分配场景给用户'}}</div>
           <div class="b-input" >
-            <input v-model="userName" :placeholder="lang==='en'?'请输入用户账号':'请输入用户账号'" type="text">
+            <input v-model="userName" :placeholder="lang==='en'?'User name':'请输入用户账号'" type="text">
           </div>
         </div>
       </div>
 
       <div class="bind-btn coo-btn" >
-        <span @click="addCooperation">{{lang==='en'?'确定':'确定'}}</span>
-        <span class="default" @click="handleClick">{{lang==='en'?'取消':'取消'}}</span>
+        <span @click="addCooperation">{{lang==='en'?'OK':'确定'}}</span>
+        <span class="default" @click="handleClick">{{lang==='en'?'Cancel':'取消'}}</span>
       </div>
     </div>
 
@@ -28,13 +28,16 @@
 </template>
 
 <script>
+import toastZH from '@/store/language/cn/toast'
+import toastEN from '@/store/language/en/toast'
 
 export default {
   props: ['sceneNum', 'visible'],
   data () {
     return {
       userName: '',
-      lang: localStorage.getItem('language')
+      lang: localStorage.getItem('language'),
+      toastCode: localStorage.getItem('language') === 'en' ? toastEN : toastZH
     }
   },
   computed: {
@@ -44,6 +47,7 @@ export default {
     visible: function (newVal) {
       this.userName = ''
       this.lang = localStorage.getItem('language')
+      this.toastCode = this.lang === 'en' ? toastEN : toastZH
     }
   },
   methods: {
@@ -53,6 +57,10 @@ export default {
     async addCooperation () {
       let token = localStorage.getItem('token')
 
+      if (!this.userName) {
+        return this.$toast.show('error', `${this.lang === 'en' ? 'Please enter user name.' : '请输入用户名'}`)
+      }
+
       let result = await this.$http({
         method: 'post',
         data: {
@@ -67,9 +75,9 @@ export default {
       let data = result.data
       if (data.code === 0) {
         this.handleClick()
-        this.$toast.show('success', '分配成功')
+        this.$toast.show('success', this.toastCode['44'])
       } else {
-        return this.$toast.show('warn', data.msg, () => {})
+        return this.$toast.show('warn', this.toastCode[data.code])
       }
     }
   }

+ 180 - 88
pc/src/page/manage/temp/scene.vue

@@ -1,64 +1,76 @@
 <template>
   <div class="scene-layout">
-    <div class="tab-search">
-      <input
-        v-model="searchKey"
-        @keyup.enter="gotoSearch(searchKey)"
-        type="text"
-        :placeholder="langScenes.placeholder.searchID"
-      />
-      <i class="iconfont icon-sousuo" @click="gotoSearch(searchKey)"></i>
+    <div class="d-header">
+        <ul class="tab-list">
+          <li @click="tabActive = item.id" :class="{active:tabActive === item.id}" v-for="(item,i) in langScenes.tabList" :key="i">
+            {{item.name}}
+          </li>
+        </ul>
+      <div class="tab-search">
+        <input
+          v-model="searchKey"
+          @keyup.enter="gotoSearch(searchKey)"
+          type="text"
+          :placeholder="langScenes.placeholder.searchID"
+        />
+        <i class="iconfont icon-sousuo" @click="gotoSearch(searchKey)"></i>
+      </div>
     </div>
+
     <ul v-if="total" ref="ulMenu">
-      <li v-for="(item,index) in myscene.list" :key="index">
-        <div @click="((item.status === 1||item.status===-2)&&item.payStatus !== -2) && goto(item.webSite)" class="a-tap">
-          <div class="share-btn" v-if="((item.status === 1||item.status===-2)&&item.payStatus !== -2)" @click.stop="handleShare(item)"></div>
-          <div class="cooperation" v-if="item.cooperationUserName"><i class="iconfont icon-geren"></i><span>{{item.cooperationUserName}}</span></div>
-          <div class="card-img" :style="{backgroundImage: `url(${getSceneImg(item)})`}"></div>
-          <div class="loading-hover" v-if="item.status === 0">
-            <div class="loading-icon">
-              <span class="refreshing-loader"></span>
-              <p>{{langScenes.share.calcule}}</p>
+      <template>
+        <li v-for="(item,index) in myscene.list" :key="index">
+          <div @click="((item.status === 1||item.status===-2)&&item.payStatus !== -2) && goto(item.webSite)" class="a-tap">
+            <div class="share-btn" v-if="((item.status === 1||item.status===-2)&&item.payStatus !== -2)" @click.stop="handleShare(item)"></div>
+            <div class="cooperation" v-if="item.cooperationUserName"><i class="iconfont icon-case_teamwork"></i><span>{{langScenes.user}}: {{item.cooperationUserName}}</span></div>
+            <div class="card-img" :style="{backgroundImage: `url(${getSceneImg(item)})`}"></div>
+            <div class="loading-hover" v-if="item.status === 0">
+              <div class="loading-icon">
+                <span class="refreshing-loader"></span>
+                <p>{{langScenes.share.calcule}}</p>
+              </div>
             </div>
-          </div>
-          <div @click.stop class="loading-hover" v-if="item.payStatus === -2">
-            <div class="loading-icon" style="width:100%">
-              <p style="font-weight:bold;" v-html="langScenes.limit.insufficient"></p>
+            <div @click.stop class="loading-hover" v-if="item.payStatus === -2">
+              <div class="loading-icon" style="width:100%">
+                <p style="font-weight:bold;" v-html="langScenes.limit.insufficient"></p>
+              </div>
+              <p class="huifu" @click.stop="rechargeTip(item)">{{langScenes.limit.recharge}}</p>
             </div>
-            <p class="huifu" @click.stop="rechargeTip(item)">{{langScenes.limit.recharge}}</p>
           </div>
-        </div>
-        <div class="name" >
-          <div class="title">{{item.sceneName}}</div>
-          <div class="oper">
-            <div @click="handleMenu(index)">
-              <span class="spot"></span>
-            </div>
-            <ul v-if="ulActive === index">
-              <li v-if="item.status === 1||item.status===-2" @click="gotoEdit(item)">{{langScenes.edit}}</li>
-              <li @click="handleCooperation(item)">{{item.cooperationUserId?'取消协作':'分配协作'}}</li>
-              <li v-if="item.status !== 0" @click="del(item)">{{langScenes.delete}}</li>
-            </ul>
-            <!-- <div v-if="item.status !== 0" @click="del(item)">
-              <span >{{langScenes.delete}}</span>
+          <div class="name" >
+            <div class="title">{{item.sceneName}}</div>
+            <div class="oper">
+              <template v-if="tabActive===1">
+                <div @click="handleMenu(index)">
+                  <span class="spot"></span>
+                </div>
+                <ul v-if="ulActive === index" :style="{minWidth: language==='en'?'150px': '90px'}">
+                  <li v-if="item.status === 1||item.status===-2" @click="gotoEdit(item)">{{langScenes.edit}}</li>
+                  <li  @click="handleCooperation(item)" v-if="item.status === 1||item.status===-2">{{item.cooperationUserId?langScenes.qxfp:langScenes.fenpei}}</li>
+                  <li v-if="item.status !== 0" @click="del(item)">{{langScenes.delete}}</li>
+                </ul>
+              </template>
+
+              <template v-if="tabActive===2">
+                <div
+                  class="b_default"
+                  v-if="item.status === 1||item.status===-2"
+                  @click="gotoEdit(item)">
+                  <span>{{langScenes.edit}}</span>
+                </div>
+              </template>
             </div>
-            <div
-              v-if="item.status === 1||item.status===-2"
-              class="primary"
-              @click="gotoEdit(item)">
-              <span>{{langScenes.edit}}</span>
-            </div> -->
           </div>
-        </div>
-        <div>
-          <span>{{langScenes.shooting}}</span>
-          <span>{{item.createTime}}</span>
-        </div>
-        <div>
-          <span>{{langScenes.id}}</span>
-          <span>{{!deviceLogin?(item.snCode||item.childName):(cameradetail.snCode||cameradetail.childName)}}</span>
-        </div>
-      </li>
+          <div>
+            <span>{{langScenes.shooting}}</span>
+            <span>{{item.createTime}}</span>
+          </div>
+          <div>
+            <span>{{langScenes.id}}</span>
+            <span>{{!deviceLogin?(item.snCode||item.childName):(cameradetail.snCode||cameradetail.childName)}}</span>
+          </div>
+        </li>
+      </template>
     </ul>
     <div class="scene-nothing" v-else>
       <img :src="`${$cdn}images/nothing.png`" />
@@ -145,7 +157,8 @@ export default {
       lheight: 480,
       url: 'https://www.4dkankan.com/showProPC.html?m=KcMeJlOr8',
       item: '',
-      ulActive: ''
+      ulActive: '',
+      tabActive: 1
     }
   },
   watch: {
@@ -160,6 +173,9 @@ export default {
           this.getList(newVal)
         }
       }
+    },
+    tabActive (newVal) {
+      this.currentPage === 1 ? this.getList() : this.currentPage = 1
     }
   },
   computed: {
@@ -230,11 +246,11 @@ export default {
         })
         let data = result.data
         if (data.code === 0) {
-          this.$toast.show('success', '取消协作成功', () => {
+          this.$toast.show('success', this.langToast['45'], () => {
             this.getList()
           })
         } else {
-          return this.$toast.show('warn', data.msg, () => {})
+          return this.$toast.show('warn', this.langToast[data.code], () => {})
         }
       } else {
         this.$toast.showCooperation(item.num, () => {
@@ -368,9 +384,13 @@ export default {
         searchKey: this.searchKey,
         cameraType
       }
-      cameraId
-        ? await this.$store.dispatch('getScanScene', params)
-        : await this.$store.dispatch('getUserScene', params)
+      if (this.tabActive === 2) {
+        await this.$store.dispatch('getCooperationScene', params)
+      } else {
+        cameraId
+          ? await this.$store.dispatch('getScanScene', params)
+          : await this.$store.dispatch('getUserScene', params)
+      }
       this.pageSize = this.myscene.pageSize
       this.total = this.myscene.total || 0
     },
@@ -406,6 +426,9 @@ export default {
 </script>
 
 <style lang="scss" scoped>
+$theme-color: #1fe4dc;
+$font-color: #2d2d2d;
+
 .huifu{
   position: absolute;
   bottom: 10px;
@@ -418,6 +441,49 @@ export default {
 }
 .scene-layout {
   width: 100%;
+  margin-top: 30px;
+  .d-header{
+    margin-left: 40px;
+    height: 30px;
+    .tab-list{
+      display: inline-block;
+      li{
+        display: inline-block;
+        margin-right: 40px;
+        cursor: pointer;
+        font-size: 14px;
+        line-height: 1.5;
+      }
+      .active{
+        color: $theme-color;
+        border-bottom: 1px solid $theme-color;
+      }
+    }
+    .tab-search{
+      float: right;
+      position: relative;
+      width: 230px;
+      padding-left: 10px;
+      margin-right: 120px;
+      border: 1px solid #ccc;
+      display: flex;
+      .iconfont{
+        width: 28px;
+        height: 28px;
+        padding: 6px;
+        cursor: pointer;
+        background: #e4e4e4;
+      }
+      input{
+        width: 100%;
+        font-size: 14px;
+        appearance: none;
+        line-height: 28px;
+        height: 28px;
+        border: 0;
+      }
+    }
+  }
   > ul {
     padding: 30px 0 0 40px;
     > li {
@@ -533,7 +599,7 @@ export default {
             min-width: 90px;
             box-shadow:0px 1px 6px rgba(0,0,0,0.16);
             >li{
-              text-align: center;
+              text-align: left;
               width: 100%;
               line-height: 2.5;
               padding: 0 10px;
@@ -543,35 +609,61 @@ export default {
               }
             }
           }
+          .b_default{
+              display: inline-block;
+              background: #fff;
+              color: #000;
+              border: 1px solid #777;
+              border-radius: 2px;
+              font-size: 12px;
+              width: 52px;
+              height: 22px;
+              line-height: 22px;
+              text-align: center;
+              vertical-align: middle;
+              margin-left: 4px;
+              -webkit-box-sizing: border-box;
+              box-sizing: border-box;
+              cursor: pointer;
+              position: relative;
+              >span{
+                position: absolute;
+                top: 50%;
+                left: 50%;
+                -webkit-transform: translate(-50%,-50%);
+                transform: translate(-50%,-50%);
+                width: 100%;
+              }
+          }
         }
       }
     }
   }
-  .tab-search {
-    float: right;
-    position: relative;
-    width: 230px;
-    padding-left: 10px;
-    border: 1px solid #ccc;
-    top: -30px;
-    right: 6vw;
-    display: flex;
-    .iconfont {
-      width: 28px;
-      height: 28px;
-      padding: 6px;
-      background: #e4e4e4;
-      cursor: pointer;
-    }
-    input {
-      width: 100%;
-      font-size: 14px;
-      appearance: none;
-      line-height: 28px;
-      height: 28px;
-      border: 0;
-    }
-  }
+  // .tab-search {
+  //   float: right;
+  //   position: relative;
+  //   width: 230px;
+  //   padding-left: 10px;
+  //   border: 1px solid #ccc;
+  //   top: -30px;
+  //   right: 6vw;
+  //   display: flex;
+  //   .iconfont {
+  //     width: 28px;
+  //     height: 28px;
+  //     padding: 6px;
+  //     background: #e4e4e4;
+  //     cursor: pointer;
+  //   }
+  //   input {
+  //     width: 100%;
+  //     font-size: 14px;
+  //     appearance: none;
+  //     line-height: 28px;
+  //     height: 28px;
+  //     border: 0;
+  //   }
+  // }
   .scene-nothing {
     width: 75%;
     padding: 42px 0 210px 0;
@@ -805,8 +897,8 @@ export default {
 @media screen and (max-width: 1400px){
   .scene-layout {
     width: 100%;
-    ul {
-      li {
+    > ul {
+     > li {
         width: 265px;
         margin-right: 35px;
         .a-tap {
@@ -820,8 +912,8 @@ export default {
 @media screen and (max-width: 1300px) {
   .scene-layout {
     width: 100%;
-    ul {
-      li {
+    > ul {
+     > li {
         width: 240px;
         margin-right: 10px;
         .a-tap {

+ 15 - 3
pc/src/store/language/cn/manage.js

@@ -19,7 +19,7 @@ export default{
   information: {
     nameArr: {
       information: '账号信息',
-      scene: '我的场景',
+      scene: '场景管理',
       order: '我的订单',
       device: '我的相机',
       consumption: '消费记录',
@@ -33,7 +33,7 @@ export default{
           name: '账号信息',
           to: {name: 'information'}
         }, {
-          name: '我的场景',
+          name: '场景管理',
           to: {name: 'scene'}
         }, {
           name: '我的相机',
@@ -61,7 +61,7 @@ export default{
       {
         name: 'security',
         items: [{
-          name: '我的场景',
+          name: '场景管理',
           to: {name: 'scene'}
         }, {
           name: '退出登录',
@@ -107,8 +107,20 @@ export default{
   myScenes: {
     edit: '编辑',
     delete: '删除',
+    fenpei: '分配协作',
+    user: '协作者',
+    qxfp: '取消协作',
     shooting: '拍摄时间:',
     id: 'S/N:',
+    tabList: [
+      {
+        name: '我的场景',
+        id: 1
+      }, {
+        name: '协作场景',
+        id: 2
+      }
+    ],
     noScenes: '暂无任何场景,赶紧去拍摄吧。',
     placeholder: {
       searchID: '搜索场景'

+ 4 - 0
pc/src/store/language/cn/toast.js

@@ -42,6 +42,8 @@ export default{
   '41': '去登录',
   '42': '暂不支持降级套餐,您可进行续费或升级套餐。',
   '43': '请绑定用户账号后,进行充值扩容。',
+  '44': '分配成功',
+  '45': '已取消协作',
 
   '4007': '用户名不存在',
   '4010': '绑定的相机不存在',
@@ -74,6 +76,8 @@ export default{
   '3016': '您没有权限,请联系管理员',
   '3017': '空文件',
   '3018': '需要上传或使用的文件不存在',
+  '3021': '账号不存在,请核对后重新输入',
+  '3022': '该场景已添加协作者,请先取消协作后再添加',
   '3019': '邮箱格式不正确',
   '8001': '订单不存在',
   '8002': '支付失败',

+ 15 - 4
pc/src/store/language/en/manage.js

@@ -18,7 +18,7 @@ export default{
   information: {
     nameArr: {
       information: 'Account Information',
-      scene: 'My Scene',
+      scene: 'Scene Management',
       order: 'My Order',
       device: 'My Cameras',
       consumption: 'Billing Records',
@@ -32,7 +32,7 @@ export default{
           name: 'Account Information',
           to: {name: 'information'}
         }, {
-          name: 'My Scene',
+          name: 'Scene Management',
           to: {name: 'scene'}
         }, {
           name: 'My Cameras',
@@ -61,7 +61,7 @@ export default{
       {
         name: 'security',
         items: [ {
-          name: 'My Scene',
+          name: 'Scene Management',
           to: {name: 'scene'}
         }, {
           name: 'Log out',
@@ -103,14 +103,25 @@ export default{
       'Recommended resolution and size: 512*512 px; less than 1 MB'
     ],
     isReceive: 'Get the latest 4DKANKAN news and deals'
-
   },
   myScenes: {
     edit: 'Edit',
     delete: 'Delete',
+    fenpei: 'Invite collaborator',
+    qxfp: 'Cancel collaborator',
+    user: 'Collaborator',
     placeholder: {
       searchID: 'Search scene'
     },
+    tabList: [
+      {
+        name: 'My Scene',
+        id: 1
+      }, {
+        name: 'My Collaborative Scene',
+        id: 2
+      }
+    ],
     id: 'S/N: ',
     shooting: 'Shooting date: ',
     noScenes: 'No Records',

+ 4 - 0
pc/src/store/language/en/toast.js

@@ -42,6 +42,8 @@ export default{
   '41': 'Log in',
   '42': 'Downgrading process is currently not supported. You can renew or upgrade the package.',
   '43': 'Please bind this camera to a user account before expanding the storage.',
+  '44': 'Collaborator added!',
+  '45': 'Collaborator canceled.',
 
   '4007': 'User name doesn\'t exist.',
   '4010': 'The bound camera doesn\'t exist.',
@@ -75,6 +77,8 @@ export default{
   '3017': 'Empty file',
   '3018': 'The file to upload or to use doesn\'t exist.',
   '3019': 'Incorrect format of Email address.',
+  '3021': 'Invalid user name, please check and give a valid one.',
+  '3022': 'There is already a collaborator for this scene. Please cancel the previous collaboration first.',
   '8001': 'Order doesn\'t exist.',
   '8002': 'Payment failure',
   '5001': 'Empty modeldata.json',

+ 15 - 0
pc/src/store/user.js

@@ -372,6 +372,21 @@ export default {
       context.commit('myScene', data.data)
     },
 
+    async getCooperationScene (context, params) {
+      let res = await http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: context.state.token
+        },
+        url: '/user/scene/cooperation/cooperationSceneList'
+      })
+
+      let data = res.data
+      if (data.code !== 0) return
+      context.commit('myScene', data.data)
+    },
+
     async getScanScene (context, params) {
       let res = await http({
         method: 'post',