Преглед на файлове

Merge branch 'question-alert' into 'dev'

Question alert

See merge request skyeye/skyeye_frontend/skyeye-admin!70
航飞 楼 преди 1 година
родител
ревизия
73e44759e6

BIN
src/assets/images/alert/video-play.png


+ 27 - 9
src/views/datamanager/alertformdata/components/common/AlertTable.vue

@@ -31,18 +31,29 @@
           {{ getNameByState(row.issueState) }}
         </template>
       </el-table-column>
-      <el-table-column label="操作" width="130" fixed="right">
+      <el-table-column label="操作" width="160" fixed="right">
         <template #default="{ row }">
-          <img src="/src/assets/images/alert/urgent.png" alt="" title="点击标记为加急问题" v-if="!isShowTab && !row.priority"
-            @click="handleUrgent(row)">
-          <img src="/src/assets/images/alert/urgent-active.png" alt="" title="点击取消问题加急"
-            v-if="!isShowTab && row.priority" @click="handleUrgent(row)">
-          <img src="/src/assets/images/alert/edit.png" alt="" v-if="isShowTab" @click="handleEdit(row)">
-          <img src="/src/assets/images/alert/show-on.png" alt="" title="点击隐藏问题展示" v-if="!row.isHide"
+          <!-- <img src="/src/assets/images/alert/show-on.png" alt="" title="点击隐藏问题展示" v-if="!row.isHide"
             @click="handleShow(row)">
           <img src="/src/assets/images/alert/show-off.png" alt="" title="点击恢复问题展示" v-if="row.isHide"
-            @click="handleShow(row)">
-          <img src="/src/assets/images/alert/delete.png" alt="" title="点击删除问题数据" @click="handleDelete(row)">
+            @click="handleShow(row)"> -->
+          <el-tooltip effect="dark" :content="row.isHide ? '点此设置为生效问题' : '点此设置为无效问题'" placement="top">
+            <el-switch :model-value="!row.isHide" @change="handleShow(row)" />
+          </el-tooltip>
+          <el-tooltip effect="dark" content="点击标记为加急问题" placement="top">
+            <img src="/src/assets/images/alert/urgent.png" alt="" v-if="!isShowTab && !row.priority"
+              @click="handleUrgent(row)">
+          </el-tooltip>
+          <el-tooltip effect="dark" content="点击取消问题加急" placement="top">
+            <img src="/src/assets/images/alert/urgent-active.png" alt="" v-if="!isShowTab && row.priority"
+              @click="handleUrgent(row)">
+          </el-tooltip>
+          <el-tooltip effect="dark" content="点击编辑问题" placement="top">
+            <img src="/src/assets/images/alert/edit.png" alt="" v-if="isShowTab" @click="handleEdit(row)">
+          </el-tooltip>
+          <el-tooltip effect="dark" content="点击删除问题数据" placement="top">
+            <img src="/src/assets/images/alert/delete.png" alt="" @click="handleDelete(row)">
+          </el-tooltip>
         </template>
       </el-table-column>
     </el-table>
@@ -153,9 +164,16 @@ onBeforeMount(() => {
 :deep(.el-table-fixed-column--right) {
   .cell {
     display: flex;
+    align-items: center;
+  }
+
+  .el-switch {
+    margin-right: 20px;
   }
 
   img {
+    width: 20px;
+    height: 20px;
     margin-right: 20px;
     cursor: pointer;
   }

+ 34 - 25
src/views/datamanager/alertformdata/components/common/DetailDialog.vue

@@ -1,6 +1,7 @@
 <template>
   <div>
-    <el-dialog v-model="visible" title="问题详情" width="80%" align-center :close-on-click-modal="false">
+    <el-dialog v-model="visible" title="问题详情" width="80%" align-center :close-on-click-modal="false"
+      @close="handleClose">
       <div class="description-box">
         <div class="title">问题描述</div>
         <p>{{ description }}</p>
@@ -8,6 +9,10 @@
       <div>
         <div class="title">问题图片/视频</div>
         <div class="media-box">
+          <div class="video-box" v-if="videoPaths">
+            <img src="@/assets/images/alert/video-play.png" @click="videoDialogVisible = true"
+              style="object-fit:contain; width:200px; height:200px; border: solid 1px #CCC; cursor: pointer;" />
+          </div>
           <div class="img-box" v-for="(imagePath, index) in imagePaths" :key="index">
             <img :src="imagePath" alt="" style="object-fit:contain; width:200px; height:200px; border: solid 1px #CCC"
               @mouseenter="handleOpenPre(imagePath, index)">
@@ -23,44 +28,33 @@
     <el-dialog v-model="dialogVisible">
       <img w-full :src="dialogImageUrl" alt="" />
     </el-dialog>
+
+    <el-dialog v-model="videoDialogVisible" class="video-dialog">
+      <video type="video/mp4" muted="true" preload="auto" :controls="true" autoplay
+        style="height: 464px;object-fit: contain;">
+        <source :src="videoPaths![0]" />
+      </video>
+    </el-dialog>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, toRefs, watch } from 'vue';
+import { ref } from 'vue';
 import { ZoomIn } from '@element-plus/icons-vue';
 
 const props = defineProps({
-  showDrawer: Boolean,
   description: String,
   imagePaths: Array<string>,
+  videoPaths: Array<string>,
 });
-const { showDrawer } = toRefs(props);
 
-const emits = defineEmits(['toggleStatus']);
-const visible = ref(false);
-
-const toggle = (newVal: boolean) => {
-  visible.value = newVal;
-};
-
-watch(
-  () => showDrawer.value,
-  (newVal) => {
-    toggle(newVal)
-  }
-);
-
-watch(
-  () => visible.value,
-  (newVal) => {
-    emits('toggleStatus', newVal)
-  }
-);
+const emits = defineEmits(['close']);
+const visible = ref(true);
 
 const showPreview = ref<boolean[]>(new Array(props.imagePaths?.values.length).fill(false));
 const dialogVisible = ref(false);
 const dialogImageUrl = ref('');
+const videoDialogVisible = ref(false);
 
 const handleOpenPre = (val, index) => {
   dialogImageUrl.value = val;
@@ -79,7 +73,9 @@ const handlePreview = () => {
   dialogVisible.value = true;
 };
 
-
+const handleClose = () => {
+  emits('close');
+}
 </script>
 
 <style scoped>
@@ -138,6 +134,12 @@ const handlePreview = () => {
 .media-box {
   display: flex;
 
+  .video-box {
+    width: 200px;
+    height: 200px;
+    margin-right: 10px;
+  }
+
   .img-box {
     position: relative;
     width: 200px;
@@ -155,6 +157,13 @@ const handlePreview = () => {
     height: 200px;
     background-color: rgba(0, 0, 0, 0.5);
   }
+}
 
+:deep(.video-dialog) {
+  .el-dialog__body {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
 }
 </style>

+ 1 - 1
src/views/datamanager/alertformdata/components/common/Pagination.vue

@@ -56,7 +56,7 @@ const props = defineProps({
   },
   pageSizes: {
     type: Array,
-    default: () => [10, 20, 30, 50],
+    default: () => [10, 20, 30, 50, 100],
   },
 });
 </script>

+ 105 - 7
src/views/datamanager/alertformdata/components/common/QuestionFormBase.vue

@@ -31,6 +31,29 @@
             <img w-full :src="dialogImageUrl" alt="Preview Image" />
           </el-dialog>
         </el-form-item>
+        <el-form-item class="pic-form-item" label="问题视频:" prop="videos">
+          <p>(仅支持上传1条视频,大小50M以内)</p>
+          <el-upload v-if="formData.videos === undefined || formData.videos.length === 0" class="avatar-uploader"
+            action="/skyeye-admin-api/issue/uploadPicture" :show-file-list="false" :on-success="handleUploadVideo"
+            :headers="getHeaders()" :data="{ bizType: 'PROBLEM_REPORT' }" :before-upload="beforeUploadVideo">
+            <el-icon class="avatar-uploader-icon">
+              <Plus />
+            </el-icon>
+          </el-upload>
+          <div class="upload-success" v-else>
+            <img src="@/assets/images/alert/video-play.png" @click="videoDialogVisible = true"
+              style="cursor: pointer;" />
+            <el-icon class="clear-video" @click="handleDeleteVideo" :size="15" color="#fff">
+              <CloseBold />
+            </el-icon>
+            <el-dialog v-model="videoDialogVisible">
+              <video type="video/mp4" muted="true" preload="auto" :controls="true" autoplay
+                style="height: 500px;object-fit: fill;">
+                <source :src="formData.videos![0]" />
+              </video>
+            </el-dialog>
+          </div>
+        </el-form-item>
         <el-form-item label="问题地点:" prop="workspaceId" :rules="{ required: true, message: '请完成必填项' }">
           <el-cascader v-model="workLocation" :options="locationOptions" :props="location" placeholder="请选择问题地点"
             clearable @change="handleCascaderChange" />
@@ -57,8 +80,8 @@
 
 <script setup lang="ts">
 import { computed, onBeforeMount, onMounted, reactive, ref } from 'vue';
-import type { FormInstance, UploadProps, UploadUserFile } from 'element-plus';
-import { Plus } from '@element-plus/icons-vue';
+import { ElMessage, type FormInstance, type UploadProps, type UploadUserFile } from 'element-plus';
+import { Plus, CloseBold } from '@element-plus/icons-vue';
 import { IssueState, sourceOptions, issueStateOptionsAdd } from './constant.question';
 import { useIssueType } from '../../hooks/useIssueType';
 import { useWorkLocation } from '../../hooks/useWorkLocation';
@@ -79,6 +102,7 @@ interface FormModel {
   issueType?: number,      // 类型
   description?: string,    // 描述
   pictures?: string[],     // 图片
+  videos?: string[],
   workshopId?: number,     // 车间id
   workshopName?: string,
   workspaceId?: number,    // 工位id
@@ -101,6 +125,7 @@ const formData = reactive<FormModel>({
   issueType: undefined,
   description: '',
   pictures: [],
+  videos: [],
   workshopId: undefined,
   workspaceId: undefined,
   createdAt: '',
@@ -110,6 +135,7 @@ const formData = reactive<FormModel>({
 const location = { expandTrigger: 'hover' as const };
 const workLocation = ref<[number | undefined, number | undefined] | []>([]);
 
+
 const handleSelectChange = () => {
   formData.issueType = undefined;
 };
@@ -142,10 +168,11 @@ const handleCopyData = () => {
   formData.issueType = props.initialData?.issueType;
   formData.description = props.initialData?.description;
   formData.pictures = props.initialData?.pictures;
+  formData.videos = props.initialData?.videos;
   formData.workshopId = props.initialData?.workshopId;
   formData.workspaceId = props.initialData?.workspaceId;
   formData.createdAt = props.initialData?.createdAt;
-  console.log(props.initialData?.issueState);
+
   switch (props.initialData?.issueState) {
     case IssueState.toAuth0:
       formData.issueState = 2;
@@ -163,7 +190,6 @@ const handleCopyData = () => {
       formData.issueState = props.initialData?.issueState;
       break;
   }
-  console.log(formData.issueState);
 
   workLocation.value = [props.initialData?.workshopId, props.initialData?.workspaceId!];
   fileList.value = props.initialData?.pictures?.map(str => ({ name: str, url: str })) || [];
@@ -208,6 +234,26 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
   dialogVisible.value = true;
 };
 
+// 视频上传
+const videoDialogVisible = ref(false);
+const handleUploadVideo = (res) => {
+  if (!formData.videos) formData.videos = [];
+  formData.videos.push(res.data.url);
+};
+const handleDeleteVideo = () => {
+  formData.videos = [];
+}
+const beforeUploadVideo: UploadProps['beforeUpload'] = (rawFile) => {
+  if (rawFile.type !== 'video/mp4') {
+    ElMessage.error('上传视频仅支持mp4格式');
+    return false
+  } else if (rawFile.size / 1024 / 1024 > 50) {
+    ElMessage.error('上传视频大小不能超过50MB');
+    return false
+  }
+  return true
+};
+
 onMounted(() => {
   handleCopyData();
 });
@@ -248,10 +294,62 @@ onBeforeMount(() => {
   :deep(.el-form-item__content) {
     display: block;
   }
+}
+
+p {
+  font-size: 10px;
+  color: #A8ABB2;
+}
+
+:deep(.avatar-uploader .el-upload) {
+  border: 1px dashed #cdd0d6;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  transition: var(--el-transition-duration-fast);
+  background-color: #fafafa;
+}
+
+:deep(.avatar-uploader .el-upload:hover) {
+  border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+  font-size: 28px;
+  color: #909399;
+  width: 148px;
+  height: 148px;
+  text-align: center;
+}
+
+.upload-success {
+  position: relative;
+
+  .clear-video {
+    width: 25px;
+    height: 25px;
+    position: absolute;
+    top: 0;
+    left: 129px;
+    cursor: pointer;
+    background-color: #000;
+    border-radius: 0 0 0 25px;
+    opacity: 0.7;
+  }
+
+  :deep(.el-dialog) {
+    width: 960px;
+    height: 540px;
+    padding: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #000;
+  }
 
-  p {
-    font-size: 10px;
-    color: #A8ABB2;
+  :deep(.el-dialog__header) {
+    display: none;
   }
 }
 </style>

+ 11 - 9
src/views/datamanager/alertformdata/components/default/Default.vue

@@ -8,9 +8,9 @@
       <div v-if="showActionBar" class="action-bar">
         <span class="num-text">已选{{ chooseNum }}项</span>
         <el-button v-if="!cancelHideFlag" :class="isActiveHide ? 'btn-active' : 'btn-normal'"
-          @click="handleHideAll">全部隐藏</el-button>
+          @click="handleHideAll">全部失效</el-button>
         <el-button v-if="cancelHideFlag" :class="isActiveCancelHide ? 'btn-active' : 'btn-normal'"
-          @click="handleCancelHideAll">取消隐藏</el-button>
+          @click="handleCancelHideAll">全部生效</el-button>
         <el-button :class="isActiveDelete ? 'btn-active' : 'btn-normal'" @click="handleDeleteAll">删除</el-button>
         <el-button v-if="!cancelUrgentFlag" :class="isActiveUrgent ? 'btn-active' : 'btn-normal'"
           @click="handleUrgentAll">标记加急</el-button>
@@ -28,8 +28,8 @@
       <Pagination v-model:page="query.pageNumber" v-model:size="query.pageSize" :total="total"
         @update:page="handlePageChange" @update:size="handleSizeChange" />
     </div>
-    <DetailDialog :show-drawer="isDetailDialogShow" :description="detailDescription" :image-paths="detailPictures"
-      @toggle-status="switchDetailDialog" />
+    <DetailDialog v-if="isDetailDialogShow" :description="detailDescription" :image-paths="detailPictures"
+      :video-paths="detailVideos" @close="closeDetailDialog" />
   </div>
 </template>
 
@@ -74,6 +74,7 @@ const isActiveCopy = ref(false);
 const isDetailDialogShow = ref(false);
 const detailDescription = ref('');
 const detailPictures = ref<string[]>([]);
+const detailVideos = ref<string[]>([]);
 // 分页
 const total = ref(0);
 
@@ -100,7 +101,7 @@ const handlePop = (selection) => {
   selection.forEach((item) => {
     if (chooseId.value.indexOf(item.id) === -1)
       chooseId.value.push(item.id);
-    // 只要有一个item.isHide === false 说明多选的这些选项不是全都隐藏,不用变成“取消隐藏”
+    // 只要有一个item.isHide === false 说明多选的这些选项不是全都隐藏/失效,不用变成“取消隐藏”/全部生效
     if (item.isHide === false) cancelHideFlag.value = false;
     // 只要有一个item.priority === 0 说明不是全都加急状态,不用变成“取消加急”
     if (item.priority === 0) cancelUrgentFlag.value = false;
@@ -127,7 +128,7 @@ const handleHideAll = () => {
     handleSelectNone();
     getTableData();
     ElMessage({
-      message: '隐藏成功',
+      message: '操作成功',
       type: 'success',
     });
     setTimeout(function () {
@@ -147,7 +148,7 @@ const handleCancelHideAll = () => {
     handleSelectNone();
     getTableData();
     ElMessage({
-      message: '取消隐藏成功',
+      message: '操作成功',
       type: 'success',
     });
     setTimeout(function () {
@@ -246,13 +247,14 @@ const handleCopyToShow = () => {
 };
 
 // 详情
-const switchDetailDialog = (show: boolean) => {
-  isDetailDialogShow.value = show;
+const closeDetailDialog = () => {
+  isDetailDialogShow.value = false;
 };
 const handleDetail = (row) => {
   isDetailDialogShow.value = true;
   detailDescription.value = row.description;
   detailPictures.value = row.pictures;
+  detailVideos.value = row.videos;
 };
 
 // 单个加急priority=1/取消加急priority=0

+ 12 - 10
src/views/datamanager/alertformdata/components/show/Show.vue

@@ -11,9 +11,9 @@
       <div v-if="showActionBar" class="action-bar">
         <span class="num-text">已选{{ chooseNum }}项</span>
         <el-button v-if="!cancelHideFlag" :class="isActiveHide ? 'btn-active' : 'btn-normal'"
-          @click="handleHideAll">全部隐藏</el-button>
+          @click="handleHideAll">全部失效</el-button>
         <el-button v-if="cancelHideFlag" :class="isActiveCancelHide ? 'btn-active' : 'btn-normal'"
-          @click="handleCancelHideAll">取消隐藏</el-button>
+          @click="handleCancelHideAll">全部生效</el-button>
         <el-button :class="isActiveDelete ? 'btn-active' : 'btn-normal'" @click="handleDeleteAll">删除</el-button>
         <span class="close-btn" @click="handleSelectNone"></span>
       </div>
@@ -25,9 +25,8 @@
       <Pagination v-model:page="query.pageNumber" v-model:size="query.pageSize" :total="total"
         @update:page="handlePageChange" @update:size="handleSizeChange" />
     </div>
-    <DetailDialog :show-drawer="isDetailDialogShow" :description="detailDescription" :image-paths="detailPictures"
-      @toggle-status="switchDetailDialog" />
-
+    <DetailDialog v-if="isDetailDialogShow" :description="detailDescription" :image-paths="detailPictures"
+      :video-paths="detailVideos" @close="closeDetailDialog" />
     <AddDrawer v-if="isAddDrawer" @close="handleAddDrawerClose" />
     <EditDrawer v-if="isEditDrawer" :initial-data="rowData" @close="handleEditDrawerClose" />
   </div>
@@ -67,6 +66,7 @@ const isActiveDelete = ref(false);
 const isDetailDialogShow = ref(false);
 const detailDescription = ref('');
 const detailPictures = ref<string[]>([]);
+const detailVideos = ref<string[]>([]);
 // 添加
 const isAddDrawer = ref(false);
 const isEditDrawer = ref(false);
@@ -96,7 +96,7 @@ const handlePop = (selection) => {
   selection.forEach((item) => {
     if (chooseId.value.indexOf(item.id) === -1)
       chooseId.value.push(item.id);
-    // 只要有一个item.isHide === false 说明多选的这些选项不是全都隐藏,不用变成“取消隐藏”
+    // 只要有一个item.isHide === false 说明多选的这些选项不是全都隐藏/失效,不用变成“取消隐藏”/全部生效
     if (item.isHide === false) cancelHideFlag.value = false;
   });
   chooseNum.value = selection.length;
@@ -121,7 +121,7 @@ const handleHideAll = () => {
     handleSelectNone();
     getTableData();
     ElMessage({
-      message: '隐藏成功',
+      message: '操作成功',
       type: 'success',
     });
     setTimeout(function () {
@@ -141,7 +141,7 @@ const handleCancelHideAll = () => {
     handleSelectNone();
     getTableData();
     ElMessage({
-      message: '隐藏成功',
+      message: '操作成功',
       type: 'success',
     });
     setTimeout(function () {
@@ -185,13 +185,15 @@ const handleDeleteAll = () => {
 };
 
 // 详情
-const switchDetailDialog = (show: boolean) => {
-  isDetailDialogShow.value = show;
+const closeDetailDialog = () => {
+  isDetailDialogShow.value = false;
 };
 const handleDetail = (row) => {
   isDetailDialogShow.value = true;
   detailDescription.value = row.description;
   detailPictures.value = row.pictures;
+  detailVideos.value = row.videos;
+  console.log(detailVideos.value);
 };
 
 // 添加