Browse Source

媒体管理-媒体详情加入列表滚动

李富豪 1 year ago
parent
commit
0407404969

+ 6 - 4
Web/src/api/custom/index.ts

@@ -5,10 +5,12 @@ const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId) |
 
 
 // Api参数类型
 // Api参数类型
 export type SignLoginApiParams = {
 export type SignLoginApiParams = {
-    userCode: string,
-    key: string,
-    sign: string,
-    workSpaceId: string,
+    username: string,
+    client_id: string,
+    timestamp: number,
+    workspace_id: string,
+    workspace_name: string,
+    signature: string,
 };
 };
 
 
 export type FetchFeedbackRecordListApiParams = {
 export type FetchFeedbackRecordListApiParams = {

+ 75 - 20
Web/src/pages/page-web/projects/media/detail/components/FileInfo.vue

@@ -129,8 +129,16 @@
         </div>
         </div>
       </div>
       </div>
       <div class="fileInfo-previewList">
       <div class="fileInfo-previewList">
-        <div class="fileInfo-previewList-item">
-          <img :src="state.info.thumbnail_url" />
+        <div class="fileInfo-previewList-button" @click="onClickScroll('LEFT')" @mousedown.prevent>
+          <VerticalRightOutlined />
+        </div>
+        <div ref="contentRef" class="fileInfo-previewList-content">
+          <img
+            :class="['fileInfo-previewList-content-item', state.currentId === item.file_id ? 'fileInfo-previewList-content-item-selected' : '']"
+            v-for="item in fileList" :src="item.thumbnail_url" @click="onClickSelected(item.file_id)" />
+        </div>
+        <div class="fileInfo-previewList-button" @click="onClickScroll('RIGHT')" @mousedown.prevent>
+          <VerticalLeftOutlined />
         </div>
         </div>
       </div>
       </div>
     </a-spin>
     </a-spin>
@@ -138,9 +146,9 @@
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
-import { reactive, onMounted } from 'vue';
+import { ref, reactive, onMounted } from 'vue';
 import { message } from 'ant-design-vue';
 import { message } from 'ant-design-vue';
-import { CloseOutlined, EnvironmentOutlined, DownloadOutlined, AimOutlined } from '@ant-design/icons-vue';
+import { CloseOutlined, EnvironmentOutlined, DownloadOutlined, AimOutlined, VerticalRightOutlined, VerticalLeftOutlined } from '@ant-design/icons-vue';
 import Panoramic from '/@/components/panoramic/index.vue';
 import Panoramic from '/@/components/panoramic/index.vue';
 import { useGMapManage } from '/@/hooks/use-g-map';
 import { useGMapManage } from '/@/hooks/use-g-map';
 import { apis } from '/@/api/custom';
 import { apis } from '/@/api/custom';
@@ -150,6 +158,7 @@ import { downloadMediaFile } from '/@/api/media';
 
 
 interface Props {
 interface Props {
   fileId: string,
   fileId: string,
+  fileList: any[],
   onClose: () => Promise<any>,
   onClose: () => Promise<any>,
 };
 };
 
 
@@ -158,6 +167,8 @@ const props = withDefaults(defineProps<Props>(), {
 });
 });
 
 
 const state = reactive({
 const state = reactive({
+  currentId: props.fileId,
+  move: 0,// 移动
   downloadLoading: false,
   downloadLoading: false,
   imgLoading: false,// 图片加载
   imgLoading: false,// 图片加载
   info: {
   info: {
@@ -181,12 +192,14 @@ const state = reactive({
   map: null, // 高德地图实例
   map: null, // 高德地图实例
 })
 })
 
 
+const contentRef = ref();
+
 // 高德地图Hook
 // 高德地图Hook
 const AmapHook = useGMapManage();
 const AmapHook = useGMapManage();
 
 
-onMounted(async () => {
+const init = async () => {
   try {
   try {
-    const res = await apis.fetchFileDetail(props.fileId);
+    const res = await apis.fetchFileDetail(state.currentId);
     state.info = res.data;
     state.info = res.data;
     if (res.data.media_type !== 4) {
     if (res.data.media_type !== 4) {
       state.imgLoading = true;
       state.imgLoading = true;
@@ -217,6 +230,10 @@ onMounted(async () => {
   } catch (e) {
   } catch (e) {
     console.error(e);
     console.error(e);
   }
   }
+}
+
+onMounted(async () => {
+  await init()
 })
 })
 
 
 // 点击下载
 // 点击下载
@@ -268,6 +285,22 @@ const onClickMapLocationReset = () => {
   map.setCenter(markerPosition);
   map.setCenter(markerPosition);
   map.setZoom(17);
   map.setZoom(17);
 }
 }
+
+// 点击滚动
+const onClickScroll = (type: 'LEFT' | 'RIGHT') => {
+  const contentElement = contentRef.value;
+  const scrollAmount = 80; // 滚动的像素数等于图片宽度
+  if (type === 'LEFT') {
+    contentElement.scrollLeft -= scrollAmount; // 向左滚动
+  } else if (type === 'RIGHT') {
+    contentElement.scrollLeft += scrollAmount; // 向右滚动
+  }
+};
+
+// 点击选中
+const onClickSelected = async (id: string) => {
+  state.currentId = id;
+}
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
@@ -297,7 +330,7 @@ const onClickMapLocationReset = () => {
   &-detail {
   &-detail {
     &-left {
     &-left {
       width: calc(100vw - 420px);
       width: calc(100vw - 420px);
-      height: calc(100vh - 120px);
+      height: calc(100vh - 130px);
       overflow: hidden;
       overflow: hidden;
 
 
       &-background {
       &-background {
@@ -312,7 +345,7 @@ const onClickMapLocationReset = () => {
 
 
     &-info {
     &-info {
       width: 100%;
       width: 100%;
-      height: calc(100vh - 120px);
+      height: calc(100vh - 130px);
       padding: 24px;
       padding: 24px;
       overflow: auto;
       overflow: auto;
 
 
@@ -402,24 +435,46 @@ const onClickMapLocationReset = () => {
 
 
   &-previewList {
   &-previewList {
     width: 100%;
     width: 100%;
-    height: 70px;
+    height: 60px;
     display: flex;
     display: flex;
-    justify-content: center;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
 
 
-    &-item {
-      width: 80px;
-      height: 60px;
-      border: 2px solid $primary;
-      border-radius: 4px;
-      margin-bottom: 10px;
-      cursor: pointer;
+    &-content {
+      width: calc(100vw - 80px);
+      display: flex;
+      overflow: auto;
+      scrollbar-width: none;
 
 
-      img {
-        width: 100%;
-        height: 100%;
+      &-item {
+        width: 80px;
+        height: 60px;
         object-fit: cover;
         object-fit: cover;
+        border: 2px solid transparent;
+        border-radius: 4px;
+        margin-right: 10px;
+        cursor: pointer;
+
+        &-selected {
+          border-color: $primary;
+        }
+      }
+
+      &-item:last-child {
+        margin: 0;
       }
       }
     }
     }
+
+    &-button {
+      width: 30px;
+      height: 60px;
+      border: 1px solid #FFFFFF;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      cursor: pointer;
+    }
   }
   }
 }
 }
 
 

+ 2 - 4
Web/src/pages/page-web/projects/media/detail/components/Search.vue

@@ -9,11 +9,10 @@
           </a-button>
           </a-button>
         </a-upload>
         </a-upload>
         <a-button style="margin-right: 10px;" type="primary" :disabled="!selectedRowKeys.length"
         <a-button style="margin-right: 10px;" type="primary" :disabled="!selectedRowKeys.length"
-          @click="onClickDownload" v-if="mode === 'table'">
+          @click="onClickDownload">
           压缩下载
           压缩下载
         </a-button>
         </a-button>
-        <a-button type="primary" danger :disabled="!selectedRowKeys.length" @click="onClickDelete"
-          v-if="mode === 'table'">
+        <a-button type="primary" danger :disabled="!selectedRowKeys.length" @click="onClickDelete">
           删除
           删除
         </a-button>
         </a-button>
       </div>
       </div>
@@ -71,7 +70,6 @@ import { ELocalStorageKey } from '/@/types'
 import moment from 'moment';
 import moment from 'moment';
 
 
 interface Props {
 interface Props {
-  mode: 'table' | 'list',
   fetchList: () => Promise<any>,
   fetchList: () => Promise<any>,
   selectedRowKeys: string[],
   selectedRowKeys: string[],
   onClickDownload: () => Promise<any>,
   onClickDownload: () => Promise<any>,

+ 55 - 98
Web/src/pages/page-web/projects/media/detail/index.vue

@@ -1,9 +1,8 @@
 <template>
 <template>
   <a-spin :spinning="state.downloadLoading" tip="下载中...">
   <a-spin :spinning="state.downloadLoading" tip="下载中...">
     <div class="mediaDetail">
     <div class="mediaDetail">
-      <Search :mode="state.mode" :fetchList="fetchList" :selectedRowKeys="state.selectedRowKeys"
-        :onClickDownload="onClickBatchDownload" :onClickDelete="onClickBatchDelete" :onClickSearch="onClickSearch"
-        :onClickReset="onClickReset" />
+      <Search :fetchList="fetchList" :selectedRowKeys="state.selectedRowKeys" :onClickDownload="onClickBatchDownload"
+        :onClickDelete="onClickBatchDelete" :onClickSearch="onClickSearch" :onClickReset="onClickReset" />
       <div style="background: #FFFFFF;padding: 20px;">
       <div style="background: #FFFFFF;padding: 20px;">
         <div class="mediaDetail-info">
         <div class="mediaDetail-info">
           <div class="mediaDetail-info-left">
           <div class="mediaDetail-info-left">
@@ -26,107 +25,75 @@
                 {{ state.selectedRowKeys.length }}/{{ paginationConfig.total }}
                 {{ state.selectedRowKeys.length }}/{{ paginationConfig.total }}
               </div>
               </div>
             </div>
             </div>
-            <a-button style="padding:0 5px;" :type="state.mode === 'table' ? 'primary' : 'default'"
-              :ghost="state.mode === 'table'" size="small" @click="state.mode = 'table'">
-              <MenuOutlined />
-            </a-button>
-            <a-button style="padding:0 5px;" :type="state.mode === 'list' ? 'primary' : 'default'"
-              :ghost="state.mode === 'list'" size="small" @click="state.mode = 'list'">
-              <AppstoreOutlined />
-            </a-button>
           </div>
           </div>
         </div>
         </div>
-        <div class="mediaDetail-table" v-if="state.mode === 'table'">
-          <a-table :scroll="{ x: '100%', y: 500 }" :childrenColumnName="null" rowKey="file_id"
-            :loading="state.listLoading" :columns="columns" :dataSource="state.list" :rowClassName="rowClassName"
-            :pagination="paginationConfig"
-            :rowSelection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }">
-            <!-- 文件夹名称 -->
-            <template #file_name="{ record }">
-              <a-tooltip :title="record.file_name">
-                <div class="fileName">
-                  <div class="fileName-cover" @click="onClickLookFile(record)">
-                    <img class="fileName-cover-img" :src="record.thumbnail_url" />
-                    <img class="fileName-cover-icon" :src="panoramaSrc" v-if="record.media_type === 3" />
-                    <img class="fileName-cover-icon" :src="videoSrc" v-else-if="record.media_type === 4" />
-                  </div>
-                  <div style="margin-left: 5px;">
-                    <a-input v-model:value="record.file_name" v-if="state.editableData[record.file_id]" />
-                    <div @click="onClickLookFile(record)" v-else>
-                      {{ record.file_name }}
-                    </div>
+        <a-table :scroll="{ x: '100%', y: 500 }" :childrenColumnName="null" rowKey="file_id"
+          :loading="state.listLoading" :columns="columns" :dataSource="state.list" :rowClassName="rowClassName"
+          :pagination="paginationConfig"
+          :rowSelection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }">
+          <!-- 文件夹名称 -->
+          <template #file_name="{ record }">
+            <a-tooltip :title="record.file_name">
+              <div class="fileName">
+                <div class="fileName-cover" @click="onClickLookFile(record)">
+                  <img class="fileName-cover-img" :src="record.thumbnail_url" />
+                  <img class="fileName-cover-icon" :src="panoramaSrc" v-if="record.media_type === 3" />
+                  <img class="fileName-cover-icon" :src="videoSrc" v-else-if="record.media_type === 4" />
+                </div>
+                <div style="margin-left: 5px;">
+                  <a-input v-model:value="record.file_name" v-if="state.editableData[record.file_id]" />
+                  <div @click="onClickLookFile(record)" v-else>
+                    {{ record.file_name }}
                   </div>
                   </div>
                 </div>
                 </div>
-              </a-tooltip>
-            </template>
-            <!-- 操作 -->
-            <template #action="{ record }">
-              <!-- 编辑态操作 -->
-              <div v-if="state.editableData[record.file_id]">
-                <a-tooltip title="确定">
-                  <CheckOutlined style="color: #28d445;margin-right: 10px;" @click="onClickSave(record)" />
-                </a-tooltip>
-                <a-tooltip title="取消">
-                  <CloseOutlined style="color: #e70102;" @click="() => delete state.editableData[record.file_id]" />
-                </a-tooltip>
               </div>
               </div>
-              <!-- 非编辑态操作 -->
-              <div class="flex-align-center flex-row" style="color: #2d8cf0" v-else>
-                <div v-if="[1, 3].includes(record.media_type)">
-                  <a-tooltip title="在地图上加载" v-if="!record.element_id">
-                    <AimOutlined style="margin-right: 10px;" @click="onClickCreateMapElement(record.file_id)" />
-                  </a-tooltip>
-                  <a-tooltip title="在地图上取消加载" v-else>
-                    <AimOutlined style="color: #e70102;margin-right: 10px;"
-                      @click="onClickDeleteMapElement(record.file_id)" />
-                  </a-tooltip>
-                </div>
-                <a-tooltip title="重命名" v-else-if="record.media_type === 4">
-                  <EditOutlined style="margin-right: 10px;" @click="onClickRechristen(record)" />
-                </a-tooltip>
-                <a-tooltip title="删除" v-else-if="record.media_type === 2">
-                  <DeleteOutlined style="margin-right: 10px;" @click="onClickDelete(record)" />
+            </a-tooltip>
+          </template>
+          <!-- 操作 -->
+          <template #action="{ record }">
+            <!-- 编辑态操作 -->
+            <div v-if="state.editableData[record.file_id]">
+              <a-tooltip title="确定">
+                <CheckOutlined style="color: #28d445;margin-right: 10px;" @click="onClickSave(record)" />
+              </a-tooltip>
+              <a-tooltip title="取消">
+                <CloseOutlined style="color: #e70102;" @click="() => delete state.editableData[record.file_id]" />
+              </a-tooltip>
+            </div>
+            <!-- 非编辑态操作 -->
+            <div class="flex-align-center flex-row" style="color: #2d8cf0" v-else>
+              <div v-if="[1, 3].includes(record.media_type)">
+                <a-tooltip title="在地图上加载" v-if="!record.element_id">
+                  <AimOutlined style="margin-right: 10px;" @click="onClickCreateMapElement(record.file_id)" />
                 </a-tooltip>
                 </a-tooltip>
-                <a-tooltip title="压缩下载">
-                  <DownloadOutlined @click="onClickDownload(record)" />
+                <a-tooltip title="在地图上取消加载" v-else>
+                  <AimOutlined style="color: #e70102;margin-right: 10px;"
+                    @click="onClickDeleteMapElement(record.file_id)" />
                 </a-tooltip>
                 </a-tooltip>
               </div>
               </div>
-            </template>
-          </a-table>
-        </div>
-        <div class="mediaDetail-list" v-else>
-          <a-list :grid="{ gutter: 16, xs: 1, sm: 2, md: 3, lg: 4, xl: 8, xxl: 12 }" :loading="state.listLoading"
-            :dataSource="state.list" :pagination="paginationConfig">
-            <template #renderItem="{ item }">
-              <a-list-item>
-                <a-card hoverable @click="onClickLookFile(item)">
-                  <template #cover>
-                    <div style="display: flex;justify-content:center;align-items: center;">
-                      <img style="width: 70%;margin: 10px;" :src="item.thumbnail_url" />
-                    </div>
-                  </template>
-                  <a-card-meta>
-                    <template #description>
-                      <div class="mediaDetail-list-name">
-                        {{ item.file_name }}
-                      </div>
-                    </template>
-                  </a-card-meta>
-                </a-card>
-              </a-list-item>
-            </template>
-          </a-list>
-        </div>
+              <a-tooltip title="重命名" v-else-if="record.media_type === 4">
+                <EditOutlined style="margin-right: 10px;" @click="onClickRechristen(record)" />
+              </a-tooltip>
+              <a-tooltip title="删除" v-else-if="record.media_type === 2">
+                <DeleteOutlined style="margin-right: 10px;" @click="onClickDelete(record)" />
+              </a-tooltip>
+              <a-tooltip title="压缩下载">
+                <DownloadOutlined @click="onClickDownload(record)" />
+              </a-tooltip>
+            </div>
+          </template>
+        </a-table>
       </div>
       </div>
     </div>
     </div>
   </a-spin>
   </a-spin>
-  <FileInfo :fileId="state.fileId" :onClose="fileInfoOnClickClose" v-if="state.fileInfoVisible" />
+  <FileInfo :fileId="state.fileId" :fileList="state.list" :onClose="fileInfoOnClickClose"
+    v-if="state.fileInfoVisible" />
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
 import { reactive, onMounted } from 'vue';
 import { reactive, onMounted } from 'vue';
 import { message } from 'ant-design-vue';
 import { message } from 'ant-design-vue';
-import { MenuOutlined, AppstoreOutlined, EditOutlined, DeleteOutlined, DownloadOutlined, AimOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons-vue';
+import { EditOutlined, DeleteOutlined, DownloadOutlined, AimOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons-vue';
 import Search from './components/Search.vue';
 import Search from './components/Search.vue';
 import FileInfo from './components/FileInfo.vue';
 import FileInfo from './components/FileInfo.vue';
 import panoramaSrc from '/@/assets/media/panorama.svg';
 import panoramaSrc from '/@/assets/media/panorama.svg';
@@ -147,7 +114,6 @@ interface State {
     [key: string]: string,
     [key: string]: string,
   },
   },
   downloadLoading: boolean,
   downloadLoading: boolean,
-  mode: 'table' | 'list',
   fileId: string,
   fileId: string,
   fileInfoVisible: boolean,
   fileInfoVisible: boolean,
 };
 };
@@ -160,7 +126,6 @@ const state: State = reactive({
   selectedRowKeys: [],
   selectedRowKeys: [],
   editableData: {},
   editableData: {},
   downloadLoading: false,
   downloadLoading: false,
-  mode: 'table',
   fileId: '',
   fileId: '',
   fileInfoVisible: false,
   fileInfoVisible: false,
 })
 })
@@ -445,14 +410,6 @@ const onClickDownload = async (record: any) => {
       }
       }
     }
     }
   }
   }
-
-  &-list {
-    &-name {
-      overflow: hidden;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-    }
-  }
 }
 }
 
 
 .ant-table {
 .ant-table {

+ 15 - 5
Web/src/router/index.ts

@@ -1,5 +1,6 @@
+import { message } from 'ant-design-vue'
 import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
 import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
-import { ERouterName } from '/@/types/index'
+import { ELocalStorageKey, ERouterName, EUserType } from '/@/types'
 import CreatePlan from '/@/components/task/CreatePlan.vue'
 import CreatePlan from '/@/components/task/CreatePlan.vue'
 import WaylinePanel from '/@/pages/page-web/projects/wayline.vue'
 import WaylinePanel from '/@/pages/page-web/projects/wayline.vue'
 import DockPanel from '/@/pages/page-web/projects/dock.vue'
 import DockPanel from '/@/pages/page-web/projects/dock.vue'
@@ -104,16 +105,25 @@ const router = createRouter({
 
 
 const updateToken = async (data: SignLoginApiParams) => {
 const updateToken = async (data: SignLoginApiParams) => {
   try {
   try {
-    await apis.signLogin(data)
+    const result = await apis.signLogin(data);
+    if (result.code === 0) {
+      localStorage.setItem(ELocalStorageKey.Token, result.data.access_token)
+      localStorage.setItem(ELocalStorageKey.WorkspaceId, result.data.workspace_id)
+      localStorage.setItem(ELocalStorageKey.Username, result.data.username)
+      localStorage.setItem(ELocalStorageKey.UserId, result.data.user_id)
+      localStorage.setItem(ELocalStorageKey.Flag, EUserType.Web.toString())
+    } else {
+      message.error(result.message)
+    }
   } catch (e) {
   } catch (e) {
     console.error(e);
     console.error(e);
   }
   }
 }
 }
 
 
 router.beforeEach(async (to, from, next) => {
 router.beforeEach(async (to, from, next) => {
-  const { sign, key, userCode, workSpaceId } = to.query as any
-  if (sign && key && userCode && workSpaceId) {
-    await updateToken({ sign, key, userCode, workSpaceId })
+  const { username, client_id, timestamp, workspace_id, workspace_name, signature } = to.query as any;
+  if (username && client_id && timestamp && workspace_id && workspace_name && signature) {
+    await updateToken({ username, client_id, timestamp, workspace_id, workspace_name, signature })
     next();
     next();
   } else {
   } else {
     next();
     next();