ソースを参照

将skyeye-admin-api写死的部分改为从配置读取

louhangfei 1 年間 前
コミット
3955b2ffd6

+ 9 - 16
src/api/camera/camera-overview.ts

@@ -183,21 +183,14 @@ export const addNVRCameraItem = (data: CameraNVRItem) => {
   });
 };
 
-
 // 批量添加相机 - 下载场景code
 export function downloadSpaceCode() {
-  return http.request({
-    url: '/addCameraList/downloadWorkspaceCodeForm',
-    method: 'get',
-    responseType: 'blob',
-  }, { isShowErrorMessage: false, isTransformResponse: false });
-};
-
-// 批量添加相机 - 下载模板
-export function downloadBatchTemplate() {
-  return http.request({
-    url: '/skyeye/CAMERALIST_TEMPLATE/camera-upload-template.xlsx',
-    method: 'get',
-    responseType: 'blob',
-  }, { urlPrefix: '/skyeye-file-upload', isShowErrorMessage: false, isTransformResponse: false });
-};
+  return http.request(
+    {
+      url: '/addCameraList/downloadWorkspaceCodeForm',
+      method: 'get',
+      responseType: 'blob',
+    },
+    { isShowErrorMessage: false, isTransformResponse: false },
+  );
+}

+ 245 - 189
src/views/cameras/overview/components/BatchImportCamera.vue

@@ -4,40 +4,80 @@
       <template #header>
         <div class="flex justify-between items-center pop-head">
           <div style="font-size: 16px; font-weight: 600">批量导入</div>
-          <el-icon :size="18" class="mr-3" @click="() => { emits('close'); }" style="cursor: pointer;">
+          <el-icon
+            :size="18"
+            class="mr-3"
+            @click="
+              () => {
+                emits('close');
+              }
+            "
+            style="cursor: pointer"
+          >
             <Close />
           </el-icon>
         </div>
       </template>
       <div class="upload-content">
-        <el-upload ref="upload" style="width: 384px; height: 192px; border-radius: 8px" :headers="headers"
-          :multiple="false" :limit="1" drag action="/skyeye-admin-api/addCameraList/uploadForm" :with-credentials="true"
-          :auto-upload="false" :before-upload="beforeUpload" :on-success="handleUploadSuccess" :on-exceed="handleExceed"
-          :on-change="handleChange" :on-remove="handleRemove">
-          <el-icon class="el-icon--upload" style="width: 33px; height: 42px; color: #409efc;">
+        <el-upload
+          ref="upload"
+          style="width: 384px; height: 192px; border-radius: 8px"
+          :headers="headers"
+          :multiple="false"
+          :limit="1"
+          drag
+          :action="actionUrl"
+          :with-credentials="true"
+          :auto-upload="false"
+          :before-upload="beforeUpload"
+          :on-success="handleUploadSuccess"
+          :on-exceed="handleExceed"
+          :on-change="handleChange"
+          :on-remove="handleRemove"
+        >
+          <el-icon class="el-icon--upload" style="width: 33px; height: 42px; color: #409efc">
             <Document />
           </el-icon>
           <div class="el-upload__text">
-            <div style="font-size: 12px; color: red; margin-bottom: 5px;">请下载模板并按要求填写后上传</div>
+            <div style="font-size: 12px; color: red; margin-bottom: 5px"
+              >请下载模板并按要求填写后上传</div
+            >
             <div style="font-size: 16px">点击或将文件拖拽到这里上传</div>
-            <div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); margin-top: 5px;">文件支持.xlsx .xls格式,仅支持上传一个文件</div>
+            <div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); margin-top: 5px"
+              >文件支持.xlsx .xls格式,仅支持上传一个文件</div
+            >
           </div>
         </el-upload>
         <div style="margin-top: 72px; margin-left: 128px; display: flex">
-          <el-icon :size="18" style="margin-top: 7px;">
+          <el-icon :size="18" style="margin-top: 7px">
             <Download />
           </el-icon>
           <el-tooltip content="点击下载场景字段对应的code信息" placement="top" effect="light">
-            <span style="color:#409efc; margin-top: 6px; margin-right: 12px; cursor: pointer;"
-              @click="handleDownloadSceneCode">场景code信息查询</span>
+            <span
+              style="color: #409efc; margin-top: 6px; margin-right: 12px; cursor: pointer"
+              @click="handleDownloadSceneCode"
+              >场景code信息查询</span
+            >
           </el-tooltip>
           <el-button @click="handleDownloadTemplate">下载模板</el-button>
-          <el-button type="primary" @click="handleImport" :disabled="isImportEnable">导入</el-button>
+          <el-button type="primary" @click="handleImport" :disabled="isImportEnable"
+            >导入</el-button
+          >
         </div>
       </div>
     </el-card>
 
-    <el-dialog v-model="DialogVisibleErr" title="Warning" width="50%" align-center @close="() => { emits('update'); }">
+    <el-dialog
+      v-model="DialogVisibleErr"
+      title="Warning"
+      width="50%"
+      align-center
+      @close="
+        () => {
+          emits('update');
+        }
+      "
+    >
       <template #header>
         <el-icon :size="24" color="#f2b20a" style="margin: 0 5px 2px">
           <WarnTriangleFilled />
@@ -45,8 +85,8 @@
         <div class="header-text">添加提示</div>
       </template>
       <div class="sum-count">
-        成功上传 <span class="succ-sum">{{ sucCount }}</span> 条,
-        失败 <span class="err-sum">{{ errCount }}</span> 条
+        成功上传 <span class="succ-sum">{{ sucCount }}</span> 条, 失败
+        <span class="err-sum">{{ errCount }}</span> 条
       </div>
       <div class="err-info">
         <ul v-for="(item, index) in errDetail" :key="index">
@@ -58,223 +98,239 @@
       </template>
     </el-dialog>
   </div>
-
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue';
-import axios, { AxiosRequestConfig } from 'axios';
-import { useUserStore } from '@/store/modules/user';
-import { genFileId, ElMessage } from 'element-plus';
-import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
-import { Close, Document, WarnTriangleFilled, Download } from '@element-plus/icons-vue';
-import { useGlobSetting } from '@/hooks/setting';
+  import { computed, ref } from 'vue';
+  import axios, { AxiosRequestConfig } from 'axios';
+  import { useUserStore } from '@/store/modules/user';
+  import { genFileId, ElMessage } from 'element-plus';
+  import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+  import { Close, Document, WarnTriangleFilled, Download } from '@element-plus/icons-vue';
+  import { useGlobSetting } from '@/hooks/setting';
+  import urlJoin from 'url-join';
 
-const emits = defineEmits(['close', 'update']);
+  const emits = defineEmits(['close', 'update']);
 
-const userStore = useUserStore();
-const headers = {
-  Satoken: userStore.getToken,
-  Tenantid: userStore.getTenantId,
-};
+  const userStore = useUserStore();
+  const headers = {
+    Satoken: userStore.getToken,
+    Tenantid: userStore.getTenantId,
+  };
 
-const upload = ref<UploadInstance>();
-const cardVisible = ref<boolean>(true);
-const isImportEnable = ref<boolean>(true);
-const DialogVisibleErr = ref<boolean>(false);
-const sucCount = ref<number>(0);
-const errCount = ref<number>(0);
-const errDetail = ref<string[]>([]);
+  const upload = ref<UploadInstance>();
+  const cardVisible = ref<boolean>(true);
+  const isImportEnable = ref<boolean>(true);
+  const DialogVisibleErr = ref<boolean>(false);
+  const sucCount = ref<number>(0);
+  const errCount = ref<number>(0);
+  const errDetail = ref<string[]>([]);
 
-const { urlPrefix } = useGlobSetting()
+  const { urlPrefix } = useGlobSetting();
 
-// 下载场景code信息查询表
-const handleDownloadSceneCode = async () => {
-  try {
-    const config: AxiosRequestConfig = {
-      headers,
-      responseType: 'blob',
-    };
-    const response = await axios.get(urlPrefix + '/addCameraList/downloadWorkspaceCodeForm', config);
-    const blob = new Blob([response.data], {
-      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
-    });
-    // 创建下载链接
-    let downloadLink: HTMLAnchorElement | null = document.createElement('a');
-    const url = window.URL.createObjectURL(blob);
-    downloadLink.href = url;
-    downloadLink.download = '场景code信息查询.xlsx';
-    downloadLink.click();
-    // 移除下载链接
-    window.URL.revokeObjectURL(url);
-    downloadLink = null;
-  } catch (error) {
-    console.error('Error downloading file:', error);
-  }
-};
+  const actionUrl = computed(() => {
+    return urlJoin(urlPrefix, `/addCameraList/uploadForm`);
+  });
 
-// 下载模板
-const handleDownloadTemplate = async () => {
-  try {
-    const config: AxiosRequestConfig = {
-      headers,
-      responseType: 'blob',
-    };
-    const response = await axios.get('/skyeye-file-upload/skyeye/CAMERALIST_TEMPLATE/camera-upload-template.xlsx', config);
-    const blob = new Blob([response.data], {
-      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
-    });
-    // 创建下载链接
-    let downloadLink: HTMLAnchorElement | null = document.createElement('a');
-    const url = window.URL.createObjectURL(blob);
-    downloadLink.href = url;
-    downloadLink.download = '相机批量添加.xlsx';
-    downloadLink.click();
-    // 移除下载链接
-    window.URL.revokeObjectURL(url);
-    downloadLink = null;
-  } catch (error) {
-    console.error('Error downloading file:', error);
-  }
-};
+  // 下载场景code信息查询表
+  const handleDownloadSceneCode = async () => {
+    try {
+      const config: AxiosRequestConfig = {
+        headers,
+        responseType: 'blob',
+      };
+      const response = await axios.get(
+        urlPrefix + '/addCameraList/downloadWorkspaceCodeForm',
+        config,
+      );
+      const blob = new Blob([response.data], {
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+      });
+      // 创建下载链接
+      let downloadLink: HTMLAnchorElement | null = document.createElement('a');
+      const url = window.URL.createObjectURL(blob);
+      downloadLink.href = url;
+      downloadLink.download = '场景code信息查询.xlsx';
+      downloadLink.click();
+      // 移除下载链接
+      window.URL.revokeObjectURL(url);
+      downloadLink = null;
+    } catch (error) {
+      console.error('Error downloading file:', error);
+    }
+  };
+
+  // 下载模板
+  const handleDownloadTemplate = async () => {
+    try {
+      const config: AxiosRequestConfig = {
+        headers,
+        responseType: 'blob',
+      };
+      const response = await axios.get(
+        './skyeye-file-upload/skyeye/CAMERALIST_TEMPLATE/camera-upload-template.xlsx',
+        config,
+      );
+      const blob = new Blob([response.data], {
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+      });
+      // 创建下载链接
+      let downloadLink: HTMLAnchorElement | null = document.createElement('a');
+      const url = window.URL.createObjectURL(blob);
+      downloadLink.href = url;
+      downloadLink.download = '相机批量添加.xlsx';
+      downloadLink.click();
+      // 移除下载链接
+      window.URL.revokeObjectURL(url);
+      downloadLink = null;
+    } catch (error) {
+      console.error('Error downloading file:', error);
+    }
+  };
 
-// 导入
-const handleImport = async () => {
-  upload.value!.submit();
-};
+  // 导入
+  const handleImport = async () => {
+    upload.value!.submit();
+  };
 
-// 上传文件之前的钩子,参数为上传的文件。即上传之前验证文件类型/后缀
-const beforeUpload = (file) => {
-  const isExcel = /\.(xlsx|xls)$/.test(file.name.toLowerCase());
-  if (!isExcel) {
-    // 提示用户选择正确的文件类型
-    ElMessage({
-      message: '仅支持上传.xlsx .xls格式文件',
-      type: 'error',
-    });
-    return false; // 阻止上传
+  // 上传文件之前的钩子,参数为上传的文件。即上传之前验证文件类型/后缀
+  const beforeUpload = (file) => {
+    const isExcel = /\.(xlsx|xls)$/.test(file.name.toLowerCase());
+    if (!isExcel) {
+      // 提示用户选择正确的文件类型
+      ElMessage({
+        message: '仅支持上传.xlsx .xls格式文件',
+        type: 'error',
+      });
+      return false; // 阻止上传
+    }
+    return true; // 允许上传
   };
-  return true; // 允许上传
-};
 
-const handleUploadSuccess = (response, _file, _fileList) => {
-  console.log(response);
-  sucCount.value = response.data.successCount;
-  errCount.value = response.data.failCount;
-  errDetail.value = response.data.errorList;
+  const handleUploadSuccess = (response, _file, _fileList) => {
+    console.log(response);
+    sucCount.value = response.data.successCount;
+    errCount.value = response.data.failCount;
+    errDetail.value = response.data.errorList;
 
-  try {
-    if (errDetail.value.length > 0) {
-      errDetail.value.forEach((item, index) => {
-        if (item.indexOf('【添加失败】') >= 0) {
-          errDetail.value[index] = item.replace('【添加失败】', '<span style="color: #ff4d4f">【添加失败】</span>')
-        } else if (item.indexOf('【添加成功】') >= 0) {
-          errDetail.value[index] = item.replace('【添加成功】', '<span style="color: #52c41a">【添加成功】</span>')
-        }
-      })
-    };
+    try {
+      if (errDetail.value.length > 0) {
+        errDetail.value.forEach((item, index) => {
+          if (item.indexOf('【添加失败】') >= 0) {
+            errDetail.value[index] = item.replace(
+              '【添加失败】',
+              '<span style="color: #ff4d4f">【添加失败】</span>',
+            );
+          } else if (item.indexOf('【添加成功】') >= 0) {
+            errDetail.value[index] = item.replace(
+              '【添加成功】',
+              '<span style="color: #52c41a">【添加成功】</span>',
+            );
+          }
+        });
+      }
 
-    if (sucCount.value != 0 && errCount.value === 0 && errDetail.value.length === 0) {
+      if (sucCount.value != 0 && errCount.value === 0 && errDetail.value.length === 0) {
+        ElMessage({
+          message: '添加成功', // 1.全部添加成功 —— failCount === 0
+          type: 'success',
+        });
+        emits('update');
+      } else {
+        DialogVisibleErr.value = true; // 2.有错误 —— 显示错误dialog
+      }
+      cardVisible.value = false;
+    } catch (error) {
       ElMessage({
-        message: '添加成功',  // 1.全部添加成功 —— failCount === 0
-        type: 'success',
+        message: '系统错误',
+        type: 'error',
       });
       emits('update');
-    } else {
-      DialogVisibleErr.value = true;    // 2.有错误 —— 显示错误dialog
-    };
-    cardVisible.value = false;
-  } catch (error) {
-    ElMessage({
-      message: '系统错误',
-      type: 'error',
-    });
-    emits('update');
+    }
   };
-};
 
-const handleErrComfirm = () => {
-  DialogVisibleErr.value = false;
-  emits('update');
-};
+  const handleErrComfirm = () => {
+    DialogVisibleErr.value = false;
+    emits('update');
+  };
 
-// 当超出只能上传一个文件的限制时,自动替换上一个文件
-const handleExceed: UploadProps['onExceed'] = (files) => {
-  upload.value!.clearFiles();
-  const file = files[0] as UploadRawFile;
-  file.uid = genFileId();
-  upload.value!.handleStart(file);
-};
+  // 当超出只能上传一个文件的限制时,自动替换上一个文件
+  const handleExceed: UploadProps['onExceed'] = (files) => {
+    upload.value!.clearFiles();
+    const file = files[0] as UploadRawFile;
+    file.uid = genFileId();
+    upload.value!.handleStart(file);
+  };
 
-const handleChange = () => {
-  isImportEnable.value = false;
-};
+  const handleChange = () => {
+    isImportEnable.value = false;
+  };
 
-const handleRemove = () => {
-  isImportEnable.value = true;
-};
+  const handleRemove = () => {
+    isImportEnable.value = true;
+  };
 </script>
 
 <style scoped>
-.upload-content {
-  margin-left: 90px;
-  margin-top: 36px;
-}
+  .upload-content {
+    margin-left: 90px;
+    margin-top: 36px;
+  }
 
-:deep(.el-dialog) {
-  padding: 0px;
-  border-radius: 5px;
+  :deep(.el-dialog) {
+    padding: 0px;
+    border-radius: 5px;
 
-  .el-dialog__header {
-    display: flex;
-    align-items: flex-end;
-    height: 70px;
-    padding: 0px 0px 10px 10px;
-    border-bottom: 1px solid #e7e7e7;
+    .el-dialog__header {
+      display: flex;
+      align-items: flex-end;
+      height: 70px;
+      padding: 0px 0px 10px 10px;
+      border-bottom: 1px solid #e7e7e7;
 
-    .header-text {
-      font-size: 20px;
+      .header-text {
+        font-size: 20px;
+      }
     }
-  }
 
-  .el-dialog__headerbtn {
-    top: 22px;
+    .el-dialog__headerbtn {
+      top: 22px;
 
-    .el-dialog__close {
-      color: black;
+      .el-dialog__close {
+        color: black;
+      }
     }
-  }
 
-  .el-dialog__body {
-    padding: 20px;
+    .el-dialog__body {
+      padding: 20px;
 
-    .sum-count {
-      margin: 10px 0 20px 20px;
-      font-size: 20px;
+      .sum-count {
+        margin: 10px 0 20px 20px;
+        font-size: 20px;
 
-      .succ-sum {
-        color: #52c41a;
+        .succ-sum {
+          color: #52c41a;
+        }
+
+        .err-sum {
+          color: #ff4d4f;
+        }
       }
 
-      .err-sum {
-        color: #ff4d4f;
+      .err-info {
+        height: 200px;
+        margin-left: 20px;
+        overflow: auto;
       }
     }
 
-    .err-info {
-      height: 200px;
-      margin-left: 20px;
-      overflow: auto;
+    .el-dialog__footer {
+      margin: 0 20px 20px 0;
     }
   }
 
-  .el-dialog__footer {
-    margin: 0 20px 20px 0;
+  li {
+    font-size: 14px;
+    margin-bottom: 2px;
   }
-}
-
-li {
-  font-size: 14px;
-  margin-bottom: 2px;
-}
 </style>

+ 391 - 285
src/views/datamanager/alertformdata/components/common/QuestionFormBase.vue

@@ -1,28 +1,73 @@
 <template>
   <div>
-    <el-drawer v-model="visible" title="问题编辑" direction="rtl" size="30%" :close-on-click-modal="false"
-      @close="handleCancel">
+    <el-drawer
+      v-model="visible"
+      title="问题编辑"
+      direction="rtl"
+      size="30%"
+      :close-on-click-modal="false"
+      @close="handleCancel"
+    >
       <el-form ref="formRef" :model="formData">
-        <el-form-item label="问题来源:" prop="source" :rules="{ required: true, message: '请完成必填项' }">
-          <el-select v-model="formData.source" placeholder="请选择问题来源" clearable @change="handleSelectChange">
-            <el-option v-for="item in sourceOptions" :key="item.value" :label="item.label" :value="item.value" />
+        <el-form-item
+          label="问题来源:"
+          prop="source"
+          :rules="{ required: true, message: '请完成必填项' }"
+        >
+          <el-select
+            v-model="formData.source"
+            placeholder="请选择问题来源"
+            clearable
+            @change="handleSelectChange"
+          >
+            <el-option
+              v-for="item in sourceOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
           </el-select>
         </el-form-item>
-        <el-form-item label="问题类型:" prop="issueType" :rules="{ required: true, message: '请完成必填项' }">
+        <el-form-item
+          label="问题类型:"
+          prop="issueType"
+          :rules="{ required: true, message: '请完成必填项' }"
+        >
           <el-select v-model="formData.issueType" placeholder="请选择问题类型" clearable>
             <el-option v-for="item in options" :label="item.name" :value="item.id" />
           </el-select>
         </el-form-item>
-        <el-form-item label="问题描述:" prop="description" :rules="{ required: true, message: '请完成必填项' }">
-          <el-input v-model="formData.description" type="textarea" maxlength="100" autosize show-word-limit
-            placeholder="请输入问题描述,不超过100个字"></el-input>
+        <el-form-item
+          label="问题描述:"
+          prop="description"
+          :rules="{ required: true, message: '请完成必填项' }"
+        >
+          <el-input
+            v-model="formData.description"
+            type="textarea"
+            maxlength="100"
+            autosize
+            show-word-limit
+            placeholder="请输入问题描述,不超过100个字"
+          ></el-input>
         </el-form-item>
-        <el-form-item class="pic-form-item" label="问题图片:" prop="pictures"
-          :rules="{ required: true, message: '请完成必填项' }">
+        <el-form-item
+          class="pic-form-item"
+          label="问题图片:"
+          prop="pictures"
+          :rules="{ required: true, message: '请完成必填项' }"
+        >
           <p>(建议尺寸192*108,大小10M以下)</p>
-          <el-upload v-model:file-list="fileList" action="/skyeye-admin-api/issue/uploadPicture"
-            list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove"
-            :on-success="handleAvatarSuccess" :headers="getHeaders()" :data="{ bizType: 'PROBLEM_REPORT' }">
+          <el-upload
+            v-model:file-list="fileList"
+            :action="actionUrl"
+            list-type="picture-card"
+            :on-preview="handlePictureCardPreview"
+            :on-remove="handleRemove"
+            :on-success="handleAvatarSuccess"
+            :headers="getHeaders()"
+            :data="{ bizType: 'PROBLEM_REPORT' }"
+          >
             <el-icon>
               <Plus />
             </el-icon>
@@ -34,39 +79,84 @@
         <el-form-item class="pic-form-item" label="问题视频:" prop="videos">
           <p>(仅支持上传1条视频,大小50M以内)</p>
           <el-upload
-            v-if="formData.videos === undefined || formData.videos === null || formData.videos && 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">
+            v-if="
+              formData.videos === undefined ||
+              formData.videos === null ||
+              (formData.videos && formData.videos.length === 0)
+            "
+            class="avatar-uploader"
+            :action="actionUrl"
+            :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;" />
+            <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;">
+              <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" />
+        <el-form-item
+          label="问题地点:"
+          prop="workspaceId"
+          :rules="{ required: true, message: '请完成必填项' }"
+        >
+          <el-cascader
+            v-model="workLocation"
+            :options="locationOptions"
+            :props="location"
+            placeholder="请选择问题地点"
+            clearable
+            @change="handleCascaderChange"
+          />
         </el-form-item>
-        <el-form-item label="问题时间:" prop="createdAt" :rules="{ required: true, message: '请完成必填项' }">
-          <el-date-picker v-model="formData.createdAt" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"
-            placeholder="请选择问题时间" />
+        <el-form-item
+          label="问题时间:"
+          prop="createdAt"
+          :rules="{ required: true, message: '请完成必填项' }"
+        >
+          <el-date-picker
+            v-model="formData.createdAt"
+            type="datetime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="请选择问题时间"
+          />
         </el-form-item>
-        <el-form-item label="问题状态:" prop="issueState" :rules="{ required: true, message: '请完成必填项' }">
+        <el-form-item
+          label="问题状态:"
+          prop="issueState"
+          :rules="{ required: true, message: '请完成必填项' }"
+        >
           <el-select v-model="formData.issueState" placeholder="请选择问题状态" clearable>
-            <el-option v-for="item in issueStateOptionsAdd" :key="item.value" :label="item.label" :value="item.value" />
+            <el-option
+              v-for="item in issueStateOptionsAdd"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
           </el-select>
         </el-form-item>
         <el-form-item>
@@ -81,277 +171,293 @@
 </template>
 
 <script setup lang="ts">
-import { computed, onBeforeMount, onMounted, reactive, ref } from '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';
-import { useUserStore } from '@/store/modules/user';
-
-enum SourceType {
-  ai = 1,
-  manual = 2
-};
-
-interface Props {
-  initialData?: FormModel
-};
-
-interface FormModel {
-  id?: number,
-  source?: number,         // 来源
-  issueType?: number,      // 类型
-  description?: string,    // 描述
-  pictures?: string[],     // 图片
-  videos?: string[],
-  workshopId?: number,     // 车间id
-  workshopName?: string,
-  workspaceId?: number,    // 工位id
-  workspaceName?: string,
-  createdAt?: string,      // 时间
-  issueState?: number,     // 状态
-};
-
-const visible = ref(true);
-const { aiOptions, manualOptions, getAIOptions, getManualOptions } = useIssueType();
-const { locationOptions, getLocationOptions, getNameByWorkid } = useWorkLocation();
-
-const props = defineProps<Props>();
-const emits = defineEmits(['saveForm', 'closeForm']);
-
-const formRef = ref<FormInstance>();
-const formData = reactive<FormModel>({
-  id: undefined,
-  source: undefined,
-  issueType: undefined,
-  description: '',
-  pictures: [],
-  videos: [],
-  workshopId: undefined,
-  workspaceId: undefined,
-  createdAt: '',
-  issueState: undefined
-});
-
-const location = { expandTrigger: 'hover' as const };
-const workLocation = ref<[number | undefined, number | undefined] | []>([]);
-
-
-const handleSelectChange = () => {
-  formData.issueType = undefined;
-};
-const options = computed(() => {
-  if (Number(formData.source) === SourceType.ai && aiOptions.value.length > 0) {
-    return aiOptions.value;
-  }
-  if (Number(formData.source) === SourceType.manual && manualOptions.value.length > 0) {
-    return manualOptions.value;
+  import { computed, onBeforeMount, onMounted, reactive, ref } from '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';
+  import { useUserStore } from '@/store/modules/user';
+  import urlJoin from 'url-join';
+  import { useGlobSetting } from '@/hooks/setting';
+
+  enum SourceType {
+    ai = 1,
+    manual = 2,
   }
-  return []
-});
-const handleCascaderChange = () => {
-  if (workLocation.value != null) {
-    formData.workshopId = workLocation.value[0];
-    formData.workspaceId = workLocation.value[1];
-    const tempString = getNameByWorkid(formData.workshopId, formData.workspaceId, locationOptions.value);
-    const strFlag = tempString?.indexOf('-');
-    formData.workshopName = tempString?.slice(0, strFlag);
-    formData.workspaceName = tempString?.slice(strFlag! + 1);
-  } else {
-    Reflect.deleteProperty(formData, "workshopId");
-    Reflect.deleteProperty(formData, "workspaceId");
+
+  interface Props {
+    initialData?: FormModel;
   }
-};
-
-const handleCopyData = () => {
-  formData.id = props.initialData?.id;
-  formData.source = props.initialData?.source;
-  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;
-
-  switch (props.initialData?.issueState) {
-    case IssueState.toAuth0:
-      formData.issueState = 2;
-      break;
-    case IssueState.toAuth1:
-      formData.issueState = 2;
-      break;
-    case IssueState.toDeal6:
-      formData.issueState = 4;
-      break;
-    case IssueState.hasDone7:
-      formData.issueState = 8;
-      break;
-    default:
-      formData.issueState = props.initialData?.issueState;
-      break;
+
+  interface FormModel {
+    id?: number;
+    source?: number; // 来源
+    issueType?: number; // 类型
+    description?: string; // 描述
+    pictures?: string[]; // 图片
+    videos?: string[];
+    workshopId?: number; // 车间id
+    workshopName?: string;
+    workspaceId?: number; // 工位id
+    workspaceName?: string;
+    createdAt?: string; // 时间
+    issueState?: number; // 状态
   }
 
-  workLocation.value = [props.initialData?.workshopId, props.initialData?.workspaceId!];
-  fileList.value = props.initialData?.pictures?.map(str => ({ name: str, url: str })) || [];
-};
-
-// 取消
-const handleCancel = () => {
-  emits('closeForm');
-};
-
-// 保存
-const handleSubmit = async (formEl: FormInstance | undefined) => {
-  if (!formEl) return
-  await formEl.validate((valid, fields) => {
-    if (valid) {
-      console.log('submit!', formData);
-      emits('saveForm', formData);
+  const visible = ref(true);
+  const { aiOptions, manualOptions, getAIOptions, getManualOptions } = useIssueType();
+  const { locationOptions, getLocationOptions, getNameByWorkid } = useWorkLocation();
+
+  const props = defineProps<Props>();
+  const emits = defineEmits(['saveForm', 'closeForm']);
+
+  const formRef = ref<FormInstance>();
+  const formData = reactive<FormModel>({
+    id: undefined,
+    source: undefined,
+    issueType: undefined,
+    description: '',
+    pictures: [],
+    videos: [],
+    workshopId: undefined,
+    workspaceId: undefined,
+    createdAt: '',
+    issueState: undefined,
+  });
+
+  const location = { expandTrigger: 'hover' as const };
+  const workLocation = ref<[number | undefined, number | undefined] | []>([]);
+
+  const { urlPrefix } = useGlobSetting();
+
+  const handleSelectChange = () => {
+    formData.issueType = undefined;
+  };
+  const options = computed(() => {
+    if (Number(formData.source) === SourceType.ai && aiOptions.value.length > 0) {
+      return aiOptions.value;
+    }
+    if (Number(formData.source) === SourceType.manual && manualOptions.value.length > 0) {
+      return manualOptions.value;
+    }
+    return [];
+  });
+  const handleCascaderChange = () => {
+    if (workLocation.value != null) {
+      formData.workshopId = workLocation.value[0];
+      formData.workspaceId = workLocation.value[1];
+      const tempString = getNameByWorkid(
+        formData.workshopId,
+        formData.workspaceId,
+        locationOptions.value,
+      );
+      const strFlag = tempString?.indexOf('-');
+      formData.workshopName = tempString?.slice(0, strFlag);
+      formData.workspaceName = tempString?.slice(strFlag! + 1);
     } else {
-      console.log('error submit!', fields)
+      Reflect.deleteProperty(formData, 'workshopId');
+      Reflect.deleteProperty(formData, 'workspaceId');
     }
-  })
-};
-
-// 图片上传
-const userStore = useUserStore();
-const getHeaders = () => {
-  return { Satoken: userStore.getToken, Tenantid: userStore.getTenantId };
-};
-const handleAvatarSuccess = (res) => {
-  if (!formData.pictures) formData.pictures = [];
-  formData.pictures.push(res.data.url);
-};
-const fileList = ref<UploadUserFile[]>([]);
-const dialogImageUrl = ref('');
-const dialogVisible = ref(false);
-const handleRemove: UploadProps['onRemove'] = (uploadFile) => {
-  const index = formData.pictures?.indexOf(uploadFile.url || '')!;
-  if (index !== -1) formData.pictures?.splice(index, 1);
-};
-const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
-  dialogImageUrl.value = uploadFile.url!;
-  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();
-});
-
-onBeforeMount(() => {
-  getAIOptions();
-  getManualOptions();
-  getLocationOptions();
-});
+  };
+
+  const handleCopyData = () => {
+    formData.id = props.initialData?.id;
+    formData.source = props.initialData?.source;
+    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;
+
+    switch (props.initialData?.issueState) {
+      case IssueState.toAuth0:
+        formData.issueState = 2;
+        break;
+      case IssueState.toAuth1:
+        formData.issueState = 2;
+        break;
+      case IssueState.toDeal6:
+        formData.issueState = 4;
+        break;
+      case IssueState.hasDone7:
+        formData.issueState = 8;
+        break;
+      default:
+        formData.issueState = props.initialData?.issueState;
+        break;
+    }
+
+    workLocation.value = [props.initialData?.workshopId, props.initialData?.workspaceId!];
+    fileList.value = props.initialData?.pictures?.map((str) => ({ name: str, url: str })) || [];
+  };
+
+  // 取消
+  const handleCancel = () => {
+    emits('closeForm');
+  };
+
+  const actionUrl = computed(() => {
+    return urlJoin(urlPrefix!, `/issue/uploadPicture`);
+  });
+
+  // 保存
+  const handleSubmit = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return;
+    await formEl.validate((valid, fields) => {
+      if (valid) {
+        console.log('submit!', formData);
+        emits('saveForm', formData);
+      } else {
+        console.log('error submit!', fields);
+      }
+    });
+  };
+
+  // 图片上传
+  const userStore = useUserStore();
+  const getHeaders = () => {
+    return { Satoken: userStore.getToken, Tenantid: userStore.getTenantId };
+  };
+  const handleAvatarSuccess = (res) => {
+    if (!formData.pictures) formData.pictures = [];
+    formData.pictures.push(res.data.url);
+  };
+  const fileList = ref<UploadUserFile[]>([]);
+  const dialogImageUrl = ref('');
+  const dialogVisible = ref(false);
+  const handleRemove: UploadProps['onRemove'] = (uploadFile) => {
+    const index = formData.pictures?.indexOf(uploadFile.url || '')!;
+    if (index !== -1) formData.pictures?.splice(index, 1);
+  };
+  const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
+    dialogImageUrl.value = uploadFile.url!;
+    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();
+  });
+
+  onBeforeMount(() => {
+    getAIOptions();
+    getManualOptions();
+    getLocationOptions();
+  });
 </script>
 
 <style scoped>
-:deep(.el-drawer__header) {
-  position: relative;
-
-  >:first-child {
-    margin-left: 32px;
-    font-weight: 600;
-    font-size: 16px;
-    color: rgba(0, 0, 0, 0.88);
+  :deep(.el-drawer__header) {
+    position: relative;
+
+    > :first-child {
+      margin-left: 32px;
+      font-weight: 600;
+      font-size: 16px;
+      color: rgba(0, 0, 0, 0.88);
+    }
+
+    .el-drawer__close-btn {
+      position: absolute;
+      color: #000;
+    }
   }
 
-  .el-drawer__close-btn {
-    position: absolute;
-    color: #000;
+  .btn-box {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    margin-top: 10px;
   }
-}
-
-.btn-box {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  margin-top: 10px;
-}
-
-.pic-form-item {
-  :deep(.el-form-item__content) {
-    display: block;
+
+  .pic-form-item {
+    :deep(.el-form-item__content) {
+      display: block;
+    }
+  }
+
+  p {
+    font-size: 10px;
+    color: #a8abb2;
   }
-}
-
-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;
+
+  :deep(.avatar-uploader .el-upload) {
+    border: 1px dashed #cdd0d6;
+    border-radius: 6px;
     cursor: pointer;
-    background-color: #000;
-    border-radius: 0 0 0 25px;
-    opacity: 0.7;
+    position: relative;
+    overflow: hidden;
+    transition: var(--el-transition-duration-fast);
+    background-color: #fafafa;
   }
 
-  :deep(.el-dialog) {
-    width: 960px;
-    height: 540px;
-    padding: 0;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    background-color: #000;
+  :deep(.avatar-uploader .el-upload:hover) {
+    border-color: var(--el-color-primary);
   }
 
-  :deep(.el-dialog__header) {
-    display: none;
+  .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;
+    }
+
+    :deep(.el-dialog__header) {
+      display: none;
+    }
   }
-}
-</style>
+</style>

+ 10 - 2
src/views/map-config/mini-map/MiniMapConfig.vue

@@ -36,7 +36,7 @@
         <div class="flex">
           <el-upload
             class="avatar-uploader flex justify-center items-center"
-            action="/skyeye-admin-api/layout/uploadPicture"
+            :action="actionUrl"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :with-credentials="true"
@@ -116,7 +116,7 @@
         <el-upload
           v-if="!hasBg"
           class="upload-icon flex justify-center items-center"
-          action="/skyeye-admin-api/layout/uploadPicture"
+          :action="actionUrl"
           :show-file-list="false"
           :before-upload="handleBeforeUpload"
           :on-success="handleAvatarSuccess"
@@ -147,6 +147,8 @@
   import { useUserStore } from '@/store/modules/user';
   import useCameraStatus from '@/views/cameras/preview/store/useCameraStatus';
   import { onBeforeRouteLeave } from 'vue-router';
+  import urlJoin from 'url-join';
+  import { useGlobSetting } from '@/hooks/setting';
 
   const cameraStatus = useCameraStatus();
   const { openInterval, closeInterval } = cameraStatus;
@@ -181,6 +183,12 @@
   const isUploadBg = ref<boolean>(true);
   const isMap = ref(false);
 
+  const { urlPrefix } = useGlobSetting();
+
+  const actionUrl = computed(() => {
+    return urlJoin(urlPrefix!, `/layout/uploadPicture`);
+  });
+
   function updataState(data, updateData) {
     for (let i = 0; i < data.length; i++) {
       const camera = data[i];

+ 9 - 3
src/views/page-config/ConfigEdit.vue

@@ -42,7 +42,7 @@
           <!-- <el-button @click="toJson">tojson</el-button> -->
           <el-upload
             class="avatar-uploader flex justify-center items-center"
-            action="/skyeye-admin-api/homepageConfig/updateCompanyPicture"
+            :action="actionUrl"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="handleBeforeUpload"
@@ -101,7 +101,7 @@
         <el-upload
           v-if="!hasBg"
           class="upload-icon flex justify-center items-center"
-          action="/skyeye-admin-api/homepageConfig/uploadCompanyPicture"
+          :action="actionUrl"
           :show-file-list="false"
           :before-upload="handleBeforeUpload"
           :on-success="handleAvatarSuccess"
@@ -166,6 +166,8 @@
   import safeParse from '@/utils/safeParse';
   import { useRouter } from 'vue-router';
   import { useUserStore } from '@/store/modules/user';
+  import urlJoin from 'url-join';
+  import { useGlobSetting } from '@/hooks/setting';
 
   const userStore = useUserStore();
   const getHeaders = () => {
@@ -180,7 +182,7 @@
 
   const pageConfig = usePageConfig();
   const { selectedCompany, scenesInfos, label, labelList, shopList, layoutId } = pageConfig;
-
+  const { urlPrefix } = useGlobSetting();
   const drawContainer = ref<HTMLDivElement>();
   const mapContainerRef = ref();
 
@@ -221,6 +223,10 @@
     mapContainerRef.value.updateLayoutTransformer();
   };
 
+  const actionUrl = computed(() => {
+    return urlJoin(urlPrefix, '/homepageConfig/updateCompanyPicture');
+  });
+
   watch(
     () => showShops,
     () => {

+ 8 - 5
src/views/page-config/component/ConfigDrawer.vue

@@ -26,11 +26,7 @@
           <el-upload
             class="pic-uploader"
             list-type="picture-card"
-            :action="
-              editShop.thumbnail
-                ? '/skyeye-admin-api/homepageConfig/updateWorkshopPicture'
-                : '/skyeye-admin-api/homepageConfig/uploadWorkshopPicture'
-            "
+            :action="uploadUrl"
             :show-file-list="false"
             :with-credentials="true"
             :before-upload="beforeAvatarUpload"
@@ -173,6 +169,13 @@
 
   const showColor = computed(() => colorRGB2Hex(editShop.value!.bgColor));
   const dialogTableVisible = ref(false);
+  const { urlPrefix } = useGlobSetting();
+
+  const uploadUrl = computed(() => {
+    return editShop.value.thumbnail
+      ? urlJoin(urlPrefix, '/homepageConfig/updateWorkshopPicture')
+      : urlJoin(urlPrefix, '/homepageConfig/uploadWorkshopPicture');
+  });
 
   const beforeAvatarUpload = (rawFile) => {
     if (rawFile.type !== 'image/jpeg') {

+ 10 - 3
src/views/system/user/component/AddUser.vue

@@ -14,7 +14,7 @@
           :multiple="false"
           :limit="1"
           drag
-          action="/skyeye-admin-api/user/import"
+          :action="importUrl"
           :headers="headers"
           :with-credentials="true"
           :auto-upload="false"
@@ -103,12 +103,14 @@
 
 <script setup lang="ts">
   import { Close, Document, CircleCheck, Warning } from '@element-plus/icons-vue';
-  import { ref } from 'vue';
+  import { computed, ref } from 'vue';
   import { genFileId, ElMessage } from 'element-plus';
   import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
   import { useUserStore } from '@/store/modules/user';
   import { onMounted } from 'vue';
   import axios, { AxiosRequestConfig } from 'axios';
+  import { useGlobSetting } from '@/hooks/setting';
+  import urlJoin from 'url-join';
 
   const userStore = useUserStore();
 
@@ -137,6 +139,7 @@
   const isSuc = ref<boolean>(true);
   const errDetail = ref<string[]>([]);
   const sucCount = ref<number>(0);
+  const { urlPrefix } = useGlobSetting();
 
   const props = defineProps<{ modelValue: boolean }>();
   const emits = defineEmits(['update:modelValue', 'change']);
@@ -146,6 +149,10 @@
   };
   const upload = ref<UploadInstance>();
 
+  const importUrl = computed(() => {
+    return urlJoin(urlPrefix, `/user/import`);
+  });
+
   const handleDownload = async () => {
     //调用后端接口
     try {
@@ -153,7 +160,7 @@
         headers,
         responseType: 'blob',
       };
-      const response = await axios.get('/skyeye-admin-api/user/downloadExcel', config);
+      const response = await axios.get(urlJoin(urlPrefix, '/user/downloadExcel'), config);
       const blob = new Blob([response.data], {
         type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
       });

+ 2 - 2
types/config.d.ts

@@ -50,7 +50,7 @@ export interface GlobConfig {
   title: string;
   apiUrl: string;
   shortName: string;
-  urlPrefix?: string;
+  urlPrefix: string;
   uploadUrl?: string;
   prodMock: boolean;
   imgUrl?: string;
@@ -94,7 +94,7 @@ export interface GlobConfig {
   // 图片上传地址
   uploadUrl?: string;
   // api 接口前缀
-  urlPrefix?: string;
+  urlPrefix: string;
   // 项目简称
   shortName: string;
   // 生产环境开启 mock