瀏覽代碼

feat: 类型调整

zhangjie 1 年之前
父節點
當前提交
ce2dfb245e

+ 1 - 1
.husky/pre-commit

@@ -2,4 +2,4 @@
 . "$(dirname "$0")/_/husky.sh"
 
 npm run lint-staged
-# npm run type:check
+npm run type:check

+ 4 - 4
src/api/base.ts

@@ -1,4 +1,4 @@
-import axios from 'axios';
+import axios, { AxiosResponse } from 'axios';
 import type {
   TeachingListPageParam,
   TeachingListPageRes,
@@ -30,7 +30,7 @@ export function ableTeaching(params: AbleParams): Promise<boolean> {
   return axios.post('/api/admin/teaching/enable', {}, { params });
 }
 // // 教学点管理-导入模板下载
-export function teachingTemplate(): Promise<Blob> {
+export function teachingTemplate(): Promise<AxiosResponse<Blob>> {
   return axios.post(
     '/api/admin/teaching/template',
     {},
@@ -56,7 +56,7 @@ export function ableAgent(params: AbleParams): Promise<boolean> {
   return axios.post('/api/admin/agent/enable', {}, { params });
 }
 // // 考点管理-导入模板下载
-export function agentTemplate(): Promise<Blob> {
+export function agentTemplate(): Promise<AxiosResponse<Blob>> {
   return axios.post(
     '/api/admin/agent/template',
     {},
@@ -82,7 +82,7 @@ export function ableRoom(params: AbleParams): Promise<boolean> {
   return axios.post('/api/admin/room/enable', {}, { params });
 }
 // // 考场管理-导入模板下载
-export function roomTemplate(): Promise<Blob> {
+export function roomTemplate(): Promise<AxiosResponse<Blob>> {
   return axios.post(
     '/api/admin/room/template',
     {},

+ 19 - 18
src/api/interceptor.ts

@@ -1,6 +1,11 @@
 import axios from 'axios';
 import type { AxiosRequestConfig, AxiosResponse } from 'axios';
-import { Message, Modal, Notification } from '@arco-design/web-vue';
+import {
+  Message,
+  MessageReturn,
+  Modal,
+  Notification,
+} from '@arco-design/web-vue';
 import { initSyncTime, fetchTime } from '../utils/syncServerTime';
 import { getAuthorization } from '../utils/crypto';
 import { DEVICE_ID, PLATFORM } from '../constants/app';
@@ -9,22 +14,18 @@ import { useUserStore } from '../store';
 
 axios.defaults.timeout = 10 * 1000;
 
-let load = null;
+let load: MessageReturn | null = null;
 // 同一时间有多个请求时,会形成队列。在第一个请求创建loading,在最后一个响应关闭loading
-const queue = [];
+const queue: Array<string | undefined> = [];
 // 防止鉴权失效之后多次弹窗。
 let unauthMsgBoxIsShow = false;
 
 const modifyConfig = (config: AxiosRequestConfig): void => {
   const userStore = useUserStore();
+  if (!config.headers) config.headers = {};
+
   if (userStore.token) {
-    const ids = {
-      userId: userStore.id,
-    };
-    Object.entries(ids).forEach(([key, val]) => {
-      if (val === null || val === 'null' || val === '') return;
-      config.headers[key] = val;
-    });
+    config.headers.userId = String(userStore.id);
 
     // 新版鉴权
     const timestamp = fetchTime();
@@ -33,13 +34,13 @@ const modifyConfig = (config: AxiosRequestConfig): void => {
         token: userStore.token,
         timestamp,
         account: userStore.sessionId,
-        uri: config.url.split('?')[0],
-        method: config.method,
+        uri: config.url?.split('?')[0] || '',
+        method: config.method as string,
       },
       'token'
     );
     config.headers.Authorization = Authorization;
-    config.headers.time = timestamp;
+    config.headers.time = String(timestamp);
   }
   config.headers.deviceId = DEVICE_ID;
   config.headers.platform = PLATFORM;
@@ -55,7 +56,7 @@ axios.interceptors.request.use(
         duration: 0,
       });
     }
-    queue.push(1);
+    queue.push(config.url);
 
     modifyConfig(config);
     return config;
@@ -64,7 +65,7 @@ axios.interceptors.request.use(
     // 关闭loading提示
     setTimeout(() => {
       queue.shift();
-      if (!queue.length) load.close();
+      if (!queue.length) load?.close();
     }, 100);
 
     Message.error({
@@ -81,7 +82,7 @@ axios.interceptors.response.use(
     // 关闭loading提示
     setTimeout(() => {
       queue.shift();
-      if (!queue.length) load.close();
+      if (!queue.length) load?.close();
     }, 100);
 
     if (response.config.responseType === 'blob') return response;
@@ -92,10 +93,10 @@ axios.interceptors.response.use(
     // 关闭loading提示
     setTimeout(() => {
       queue.shift();
-      if (!queue.length) load.close();
+      if (!queue.length) load?.close();
     }, 100);
 
-    console.dir(error);
+    // console.dir(error);
 
     if (!error.response) {
       let message = error.message || '无响应错误';

+ 5 - 5
src/api/order.ts

@@ -1,4 +1,4 @@
-import axios from 'axios';
+import axios, { AxiosResponse } from 'axios';
 import type {
   OptionItem,
   TaskListPageParam,
@@ -23,7 +23,7 @@ export function teachingQuery(): Promise<OptionItem[]> {
   return axios.post('/api/apply/teaching/list', {});
 }
 // 通用查询-考点查询
-export function agentQuery(teachingId: string): Promise<OptionItem[]> {
+export function agentQuery(teachingId: number): Promise<OptionItem[]> {
   return axios.post(
     '/api/apply/agent/list',
     {},
@@ -71,7 +71,7 @@ export function studentImportListPage(): Promise<StudentExportListPageRes> {
   return axios.post('/api/admin/std/page', {});
 }
 // 考生信息导入-导入模板下载
-export function studentInfoTemplate(): Promise<Blob> {
+export function studentInfoTemplate(): Promise<AxiosResponse<Blob>> {
   return axios.get('/api/admin/std/import/template', {
     responseType: 'blob',
   });
@@ -95,7 +95,7 @@ export function orderRecordAutoAssign(params: {
   return axios.post('/api/apply/std/auto/assign', {}, { params });
 }
 // 预约名单详情-导入模板下载
-export function orderRecordTemplate(): Promise<Blob> {
+export function orderRecordTemplate(): Promise<AxiosResponse<Blob>> {
   return axios.post(
     '/api/apply/imp/template',
     {},
@@ -107,7 +107,7 @@ export function orderRecordTemplate(): Promise<Blob> {
 // 预约名单详情-打印签到表
 export function orderRecordPrint(params: {
   teachingId?: number;
-}): Promise<Blob> {
+}): Promise<AxiosResponse<Blob>> {
   return axios.post(
     '/api/apply/std/auto/sign/in/print',
     {},

+ 32 - 21
src/components/file-upload/index.vue

@@ -14,7 +14,7 @@
       :data="uploadDataDict"
       :show-file-list="false"
       :auto-upload="autoUpload"
-      :custom-request="upload"
+      :custom-request="customRequest"
       :disabled="disabled"
       :on-before-upload="handleBeforeUpload"
       :limit="1"
@@ -40,7 +40,12 @@
 <script setup lang="ts">
   import { ref } from 'vue';
   import { fileMD5 } from '@/utils/md5';
-  import { FileItem, Message, RequestOption } from '@arco-design/web-vue';
+  import {
+    FileItem,
+    Message,
+    RequestOption,
+    UploadRequest,
+  } from '@arco-design/web-vue';
   import axios from 'axios';
 
   defineOptions({
@@ -80,8 +85,8 @@
   const uploadRef = ref();
   const attachmentName = ref('');
   const canUpload = ref(false);
-  const uploadDataDict = ref({ md5: '' });
-  const headers = ref({});
+  const uploadDataDict = ref({});
+  const headers = ref({ md5: '' });
   const result = ref({ success: true, message: '' });
   const loading = ref(false);
 
@@ -90,16 +95,16 @@
     uploadRef.value?.submit();
   }
 
-  function checkFileFormat(fileType) {
-    const fileFormat = fileType.split('.').pop().toLocaleLowerCase();
+  function checkFileFormat(fileType: string) {
+    const fileFormat = fileType.split('.').pop()?.toLocaleLowerCase();
     return props.format.some((item) => item.toLocaleLowerCase() === fileFormat);
   }
 
   function handleFileChange(fileList: FileItem[]) {
     if (props.autoUpload || !fileList.length) return;
 
-    attachmentName.value = fileList[0].name;
-    canUpload.value = fileList[0].status === 'init';
+    attachmentName.value = fileList[0]?.name || '';
+    canUpload.value = fileList[0]?.status === 'init';
   }
 
   async function handleBeforeUpload(file: File) {
@@ -126,33 +131,39 @@
     return true;
   }
 
-  async function upload(option: RequestOption) {
+  function customRequest(option: RequestOption): UploadRequest {
     const { onProgress, onError, onSuccess, fileItem, data } = option;
 
     const formData = new FormData();
-    Object.entries(data).forEach(([k, v]) => {
+    const paramData: Record<string, any> = data || {};
+    Object.entries(paramData).forEach(([k, v]) => {
       formData.append(k, v);
     });
-    formData.append(props.uploadFileAlias, fileItem.file);
+    formData.append(props.uploadFileAlias, fileItem.file as File);
     emit('uploading');
+    const uploadController = new AbortController();
 
-    const res = await axios
-      .post(option.action, formData, {
+    axios
+      .post(option.action as string, formData, {
         headers: option.headers,
-        onUploadProgress: (progress) => {
-          onProgress(progress);
+        signal: uploadController.signal,
+        onUploadProgress: ({ loaded, total }) => {
+          onProgress(Math.floor((100 * loaded) / total));
         },
       })
+      .then((res) => {
+        onSuccess(res);
+      })
       .catch((error: Error) => {
         onError(error);
       });
-    if (!res) return;
-    onSuccess(res);
-  }
 
-  function handleError(error: Error) {
-    console.log(error);
+    return {
+      abort: uploadController.abort,
+    };
+  }
 
+  function handleError() {
     canUpload.value = false;
     loading.value = false;
     result.value = {
@@ -196,7 +207,7 @@
     Message.error(content);
     emit('validError', result.value);
   }
-  function setAttachmentName(name) {
+  function setAttachmentName(name: string) {
     attachmentName.value = name;
   }
 

+ 27 - 18
src/components/import-dialog/index.vue

@@ -35,7 +35,7 @@
           :data="uploadDataDict"
           :show-file-list="true"
           :auto-upload="autoUpload"
-          :custom-request="upload"
+          :custom-request="customRequest"
           :disabled="disabled"
           :on-before-upload="handleBeforeUpload"
           :file-list="customFileList"
@@ -76,7 +76,12 @@
 <script setup lang="ts">
   import { computed, ref } from 'vue';
   import { fileMD5 } from '@/utils/md5';
-  import { FileItem, Message, RequestOption } from '@arco-design/web-vue';
+  import {
+    FileItem,
+    Message,
+    RequestOption,
+    UploadRequest,
+  } from '@arco-design/web-vue';
   import axios from 'axios';
 
   import useModal from '@/hooks/modal';
@@ -132,8 +137,8 @@
 
   const uploadRef = ref();
   const canUpload = ref(false);
-  const uploadDataDict = ref({ md5: '' });
-  const headers = ref({});
+  const uploadDataDict = ref({});
+  const headers = ref({ md5: '' });
   const result = ref({ success: true, message: '' });
   const loading = ref(false);
   const customFileList = ref<FileItem[]>([]);
@@ -158,8 +163,8 @@
     uploadRef.value?.submit();
   }
 
-  function checkFileFormat(fileType) {
-    const fileFormat = fileType.split('.').pop().toLocaleLowerCase();
+  function checkFileFormat(fileType: string) {
+    const fileFormat = fileType.split('.').pop()?.toLocaleLowerCase();
     return props.format.some((item) => item.toLocaleLowerCase() === fileFormat);
   }
 
@@ -202,35 +207,39 @@
     return true;
   }
 
-  async function upload(option: RequestOption) {
+  function customRequest(option: RequestOption): UploadRequest {
     const { onProgress, onError, onSuccess, fileItem, data } = option;
 
     const formData = new FormData();
-    Object.entries(data).forEach(([k, v]) => {
+    const paramData: Record<string, any> = data || {};
+    Object.entries(paramData).forEach(([k, v]) => {
       formData.append(k, v);
     });
-    formData.append(props.uploadFileAlias, fileItem.file);
+    formData.append(props.uploadFileAlias, fileItem.file as File);
     emit('uploading');
+    const uploadController = new AbortController();
 
-    let res = true;
-    await axios
-      .post(option.action, formData, {
+    axios
+      .post(option.action as string, formData, {
         headers: option.headers,
+        signal: uploadController.signal,
         onUploadProgress: ({ loaded, total }) => {
           onProgress(Math.floor((100 * loaded) / total));
         },
       })
+      .then((res) => {
+        onSuccess(res);
+      })
       .catch((error: Error) => {
-        res = false;
         onError(error);
       });
-    if (!res) return;
-    onSuccess(res);
-  }
 
-  function handleError(error: Error) {
-    console.log(error);
+    return {
+      abort: uploadController.abort,
+    };
+  }
 
+  function handleError() {
     canUpload.value = false;
     loading.value = false;
     result.value = {

+ 6 - 4
src/components/select-agent/index.vue

@@ -20,14 +20,15 @@
   defineOptions({
     name: 'SelectAgent',
   });
+  type ValueType = number | Array<number>;
 
   const props = defineProps<{
-    modelValue: number | string;
+    modelValue: ValueType;
     clearable?: boolean;
     disabled?: boolean;
     placeholder?: string;
     multiple?: boolean;
-    teachingId: number | string;
+    teachingId: number | null;
     prefix?: boolean;
   }>();
   const emit = defineEmits(['update:modelValue', 'change']);
@@ -38,7 +39,7 @@
     label: string;
   }
 
-  const selected = ref('');
+  const selected = ref<ValueType>();
   const optionList = ref<OptionListItem[]>([]);
   const search = async () => {
     optionList.value = [];
@@ -54,7 +55,8 @@
   const onChange = () => {
     const selectedData = props.multiple
       ? optionList.value.filter(
-          (item) => selected.value && selected.value.includes(item.value)
+          (item) =>
+            selected.value && (selected.value as number[]).includes(item.value)
         )
       : optionList.value.filter((item) => selected.value === item.value);
     emit('update:modelValue', selected.value);

+ 11 - 10
src/components/select-range-datetime/index.vue

@@ -25,7 +25,7 @@
 
   const props = withDefaults(
     defineProps<{
-      modelValue: object;
+      modelValue: Record<string, any>;
       keys?: KeysType;
       showTime?: boolean;
       format?: string;
@@ -44,17 +44,18 @@
   const emit = defineEmits(['update:modelValue', 'change']);
   const attrs = useAttrs();
 
-  const selected = ref([null, null]);
+  const selected = ref<number[]>();
 
-  function onChange(value) {
+  function onChange(value: any) {
     const { startTime, endTime } = props.keys;
-    const dt = {};
-    if (value) {
-      dt[startTime] = value[0];
-      dt[endTime] = value[1];
+    const dt: Record<string, number | undefined> = {};
+    const vals = value as number[] | undefined;
+    if (vals) {
+      dt[startTime] = vals[0];
+      dt[endTime] = vals[1];
     } else {
-      dt[startTime] = null;
-      dt[endTime] = null;
+      dt[startTime] = undefined;
+      dt[endTime] = undefined;
     }
     objModifyAssign(props.modelValue, dt);
     emit('update:modelValue', props.modelValue);
@@ -65,7 +66,7 @@
     () => props.modelValue,
     (val) => {
       const { startTime, endTime } = props.keys;
-      selected.value = [val[startTime], val[endTime]];
+      selected.value = [val[startTime] as number, val[endTime] as number];
     },
     {
       immediate: true,

+ 11 - 10
src/components/select-range-time/index.vue

@@ -24,7 +24,7 @@
 
   const props = withDefaults(
     defineProps<{
-      modelValue: object;
+      modelValue: Record<string, any>;
       keys?: KeysType;
       format?: string;
     }>(),
@@ -42,17 +42,18 @@
   const emit = defineEmits(['update:modelValue', 'change']);
   const attrs = useAttrs();
 
-  const selected = ref([null, null]);
+  const selected = ref<string[]>();
 
-  function onChange(value) {
+  function onChange(value: any) {
     const { startTime, endTime } = props.keys;
-    const dt = {};
-    if (value) {
-      dt[startTime] = value[0];
-      dt[endTime] = value[1];
+    const dt: Record<string, string> = {};
+    const vals = value as string[] | undefined;
+    if (vals) {
+      dt[startTime] = vals[0];
+      dt[endTime] = vals[1];
     } else {
-      dt[startTime] = null;
-      dt[endTime] = null;
+      dt[startTime] = '';
+      dt[endTime] = '';
     }
     objModifyAssign(props.modelValue, dt);
     emit('update:modelValue', props.modelValue);
@@ -63,7 +64,7 @@
     () => props.modelValue,
     (val) => {
       const { startTime, endTime } = props.keys;
-      selected.value = [val[startTime], val[endTime]];
+      selected.value = [val[startTime] as string, val[endTime] as string];
     },
     {
       immediate: true,

+ 5 - 3
src/components/select-task/index.vue

@@ -20,9 +20,10 @@
   defineOptions({
     name: 'SelectTask',
   });
+  type ValueType = number | Array<number>;
 
   const props = defineProps<{
-    modelValue: number | string;
+    modelValue: ValueType;
     clearable?: boolean;
     disabled?: boolean;
     placeholder?: string;
@@ -37,7 +38,7 @@
     label: string;
   }
 
-  const selected = ref('');
+  const selected = ref<ValueType>();
   const optionList = ref<OptionListItem[]>([]);
   const search = async () => {
     optionList.value = [];
@@ -52,7 +53,8 @@
   const onChange = () => {
     const selectedData = props.multiple
       ? optionList.value.filter(
-          (item) => selected.value && selected.value.includes(item.value)
+          (item) =>
+            selected.value && (selected.value as number[]).includes(item.value)
         )
       : optionList.value.filter((item) => selected.value === item.value);
     emit('update:modelValue', selected.value);

+ 6 - 3
src/components/select-teaching/index.vue

@@ -21,8 +21,10 @@
     name: 'SelectTeaching',
   });
 
+  type ValueType = number | Array<number>;
+
   const props = defineProps<{
-    modelValue: number | string;
+    modelValue: ValueType;
     clearable?: boolean;
     disabled?: boolean;
     placeholder?: string;
@@ -37,7 +39,7 @@
     label: string;
   }
 
-  const selected = ref('');
+  const selected = ref<ValueType>();
   const optionList = ref<OptionListItem[]>([]);
   const search = async () => {
     optionList.value = [];
@@ -52,7 +54,8 @@
   const onChange = () => {
     const selectedData = props.multiple
       ? optionList.value.filter(
-          (item) => selected.value && selected.value.includes(item.value)
+          (item) =>
+            selected.value && (selected.value as number[]).includes(item.value)
         )
       : optionList.value.filter((item) => selected.value === item.value);
     emit('update:modelValue', selected.value);

+ 6 - 2
src/constants/app.ts

@@ -2,7 +2,11 @@ import { md5 } from 'js-md5';
 
 export const PLATFORM = 'WEB';
 
+const getDevice = () => {
+  return md5(`${Math.random()}-${Date.now()}`);
+};
+
 if (!localStorage.getItem('deviceId')) {
-  localStorage.setItem('deviceId', md5(`${Math.random()}-${Date.now()}`));
+  localStorage.setItem('deviceId', getDevice());
 }
-export const DEVICE_ID = localStorage.getItem('deviceId');
+export const DEVICE_ID = localStorage.getItem('deviceId') as string;

+ 3 - 2
src/hooks/dict-option.ts

@@ -19,8 +19,9 @@ type DictTypeType = keyof typeof dicts;
 export default function useDictOption(dictType: DictTypeType) {
   const optionList = ref<Options[]>(dictToOption(dicts[dictType] || {}));
 
-  function getLabel(val): string {
-    return (dicts[dictType][val] as string) || DEFAULT_LABEL;
+  function getLabel(val: string): string {
+    // @ts-ignore
+    return dicts[dictType] ? dicts[dictType][val] : DEFAULT_LABEL;
   }
 
   return {

+ 0 - 45
src/hooks/permission.ts

@@ -1,45 +0,0 @@
-import { useAppStore } from '@/store';
-import { reactive } from 'vue';
-import { useRoute } from 'vue-router';
-
-export default function usePermission() {
-  const appStore = useAppStore();
-  const route = useRoute();
-  const permissions = reactive<Record<string, boolean>>({});
-
-  function checkPrivilege(type, field, routeAlias = null) {
-    const routerName = routeAlias || route.name;
-    const keys = field
-      .split(',')
-      .map((item) => `${type}_${item}`.toLowerCase());
-
-    return keys.some((key) => appStore.privilegeMap[routerName].includes(key));
-  }
-
-  function updatePermission() {
-    const validKeys = appStore.privilegeMap[route.name];
-
-    validKeys.forEach((key) => {
-      permissions[key] = true;
-    });
-  }
-  updatePermission();
-
-  return {
-    checkPrivilege,
-    updatePermission,
-    permissions,
-  };
-}
-
-/*
-v-if="permissions.condition_condition"
-v-if="permissions.button_select"
-v-if="permissions.button_add"
-v-if="permissions.button_export"
-v-if="permissions.button_import"
-v-if="permissions.link_preview"
-v-if="permissions.link_edit"
-v-if="permissions.link_delete"
-v-if="permissions.link_enable"
- */

+ 0 - 29
src/hooks/request/role.ts

@@ -1,29 +0,0 @@
-import { ref } from 'vue';
-import { userRoleListPage } from '@/api/base';
-import { RoleItem } from '@/api/types/base';
-import { Options } from '@/types/global';
-
-export default function useRole(toOption = false) {
-  type DataItemType = Options | RoleItem;
-  const dataList = ref<DataItemType[]>([]);
-
-  async function updateList() {
-    const data = await userRoleListPage();
-    dataList.value = data || [];
-
-    if (toOption) {
-      dataList.value = dataList.value.map((item) => {
-        return {
-          value: item.id,
-          label: item.name,
-        };
-      });
-    }
-  }
-  updateList();
-
-  return {
-    dataList,
-    updateList,
-  };
-}

+ 0 - 1
src/hooks/responsive.ts

@@ -16,7 +16,6 @@ export default function useResponsive(immediate?: boolean) {
     if (!document.hidden) {
       const isMobile = queryDevice();
       appStore.toggleDevice(isMobile ? 'mobile' : 'desktop');
-      appStore.toggleMenu(isMobile);
     }
   }
   const debounceFn = useDebounceFn(resizeHandler, 100);

+ 4 - 4
src/hooks/sms.ts

@@ -7,9 +7,9 @@ export default function useSms(name = 'sms') {
   const isFetchingCode = ref(false);
   const codeContent = ref('获取验证码');
   const nameWaitTime = ref('');
-  let tSetT = null;
+  let tSetT: NodeJS.Timer | null = null;
 
-  function setWaitingTime(wt) {
+  function setWaitingTime(wt: string) {
     nameWaitTime.value = wt;
     const codetime = lls.get(wt);
     if (codetime) {
@@ -27,7 +27,7 @@ export default function useSms(name = 'sms') {
     if (!isFetchingCode.value) return;
     codeContent.value = `倒计时${codeWaitingTime.value}s`;
 
-    const circleTime = (time) => {
+    const circleTime = (time: number) => {
       tSetT = setInterval(() => {
         if (time > 1) {
           time--;
@@ -54,7 +54,7 @@ export default function useSms(name = 'sms') {
     lls.remove(nameWaitTime.value);
     codeContent.value = '获取验证码';
     isFetchingCode.value = false;
-    clearInterval(tSetT);
+    if (tSetT) clearInterval(tSetT);
   }
 
   return {

+ 11 - 12
src/hooks/table.ts

@@ -1,15 +1,15 @@
 import { ref } from 'vue';
 import { PageResult } from '@/api/types/common';
 
-export default function useTable<T>(
-  apiFunc: (any) => Promise<PageResult<T>>,
-  searchModel: object,
+export default function useTable<T extends Record<string, any>>(
+  apiFunc: (data: any) => Promise<PageResult<T>>,
+  searchModel: Record<string, any>,
   initAutoFetch = false
 ) {
   const pageNumber = ref(1);
   const pageSize = ref(10);
   const total = ref(0);
-  const dataList = ref<T[]>([]);
+  const dataList = ref<T[]>();
 
   async function getList() {
     const datas = {
@@ -33,31 +33,30 @@ export default function useTable<T>(
     await toPage(1);
   }
 
-  function getRowIndex(index) {
+  function getRowIndex(index: number) {
     return pageSize.value * (pageNumber.value - 1) + index + 1;
   }
 
   function deletePageLastItem(len = 1) {
     let page = pageNumber.value || 1;
-    if (dataList.value.length === len) {
+    if (dataList.value && dataList.value.length === len) {
       page = page > 1 ? page - 1 : 1;
     }
     toPage(page);
   }
 
-  const pagination = ref({
-    total,
-    current: pageNumber,
-    pageSize,
+  const pagination = {
+    total: total.value,
+    current: pageNumber.value,
+    pageSize: pageSize.value,
     showTotal: true,
     showJumper: true,
     showPageSize: true,
     onChange: toPage,
     onPageSizeChange: pageSizeChange,
-  });
+  };
 
   return {
-    total,
     dataList,
     pagination,
     getRowIndex,

+ 2 - 2
src/layout/default-layout.vue

@@ -116,11 +116,11 @@
   // const ResetPwdRef = ref();
 
   function initData() {
-    curRouteName.value = route.name;
+    curRouteName.value = route.name as string;
     // console.log(route.name);
   }
 
-  function toMenuItem(val) {
+  function toMenuItem(val: string) {
     router.push({ name: val });
   }
 

+ 1 - 1
src/router/guard/permission.ts

@@ -27,7 +27,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
       return;
     }
 
-    if (!appStore.validRoutes.includes(to.name)) {
+    if (!to.name || !appStore.validRoutes.includes(to.name as string)) {
       next(NOT_FOUND);
     } else {
       next();

+ 14 - 10
src/store/modules/app/index.ts

@@ -13,16 +13,17 @@ function getMenu(privilegeData: UserMenuItem[]): {
   menuList: AppMenuItem[];
   validRoutes: string[];
 } {
-  const getChildren = (id) => {
+  const getChildren = (id: number) => {
     return privilegeData
       .filter((item) => item.parentId === id)
       .map((item) => {
-        return { ...item };
+        const nitem: AppMenuItem = { ...item, children: [] };
+        return nitem;
       });
   };
-  let menuList = getChildren('-1');
-  const validRoutes = [];
-  const toTree = (data: PrivilegeItem[]) => {
+  let menuList = getChildren(-1);
+  const validRoutes: string[] = [];
+  const toTree = (data: AppMenuItem[]) => {
     data.forEach((menu) => {
       const children = getChildren(menu.id);
       if (children.length) {
@@ -44,9 +45,9 @@ const useAppStore = defineStore('app', {
   state: (): AppState => ({
     version: '',
     appMenus: [],
-    privilegeList: [],
     validRoutes: [],
     breadcrumbs: [],
+    device: 'desktop',
   }),
 
   getters: {
@@ -62,10 +63,10 @@ const useAppStore = defineStore('app', {
     setInfo(partial: Partial<AppState>) {
       this.$patch(partial);
     },
-    fetchServerMenu(type: RoleType) {
-      const userMenus = menus.filter(
-        (menu) => !menu.roles || menu.roles.includes(type)
-      );
+    fetchServerMenu(type: RoleType | null) {
+      const userMenus = type
+        ? menus.filter((menu) => !menu.roles || menu.roles.includes(type))
+        : ([] as typeof menus);
       // // 开发时不管权限
       // console.log(type);
       // const userMenus = menus;
@@ -84,6 +85,9 @@ const useAppStore = defineStore('app', {
 
       return { name: firstRouteName };
     },
+    toggleDevice(device: string) {
+      this.device = device;
+    },
   },
   persist: {
     storage: sessionStorage,

+ 18 - 18
src/store/modules/app/menuData.ts

@@ -1,88 +1,88 @@
 export const menus = [
   {
-    id: '1',
+    id: 1,
     name: '考试预约管理',
     url: 'order',
     type: 'MENU',
-    parentId: '-1',
+    parentId: -1,
     sequence: 1,
     enable: true,
   },
   {
-    id: '2',
+    id: 2,
     name: '预约任务管理',
     url: 'TaskManage',
     type: 'MENU',
-    parentId: '1',
+    parentId: 1,
     sequence: 1,
     enable: true,
     roles: ['ADMIN'],
   },
   {
-    id: '3',
+    id: 3,
     name: '考生信息导入',
     url: 'StudentImport',
     type: 'MENU',
-    parentId: '1',
+    parentId: 1,
     sequence: 2,
     enable: true,
     roles: ['ADMIN'],
   },
   {
-    id: '4',
+    id: 4,
     name: '考生管理',
     url: 'StudentManage',
     type: 'MENU',
-    parentId: '1',
+    parentId: 1,
     sequence: 3,
     enable: true,
     roles: [],
   },
   {
-    id: '5',
+    id: 5,
     name: '预约明细',
     url: 'OrderRecordManage',
     type: 'MENU',
-    parentId: '1',
+    parentId: 1,
     sequence: 4,
     enable: true,
     roles: ['ADMIN', 'TEACHING'],
   },
   {
-    id: '11',
+    id: 11,
     name: '基础信息管理',
     url: 'base',
     type: 'MENU',
-    parentId: '-1',
+    parentId: -1,
     sequence: 2,
     enable: true,
   },
   {
-    id: '12',
+    id: 12,
     name: '教学点管理',
     url: 'TeachingManage',
     type: 'MENU',
-    parentId: '11',
+    parentId: 11,
     sequence: 1,
     enable: true,
     roles: [],
   },
   {
-    id: '13',
+    id: 13,
     name: '考点管理',
     url: 'AgentManage',
     type: 'MENU',
-    parentId: '11',
+    parentId: 11,
     sequence: 2,
     enable: true,
     roles: [],
   },
   {
-    id: '14',
+    id: 14,
     name: '考场管理',
     url: 'RoomManage',
     type: 'MENU',
-    parentId: '11',
+    parentId: 11,
     sequence: 3,
     enable: true,
     roles: [],

+ 12 - 3
src/store/modules/app/types.ts

@@ -3,17 +3,26 @@ export interface UserMenuItem {
   name: string;
   url: string;
   type: string;
-  parentId: string;
+  parentId: number;
   sequence: number;
   enable: boolean;
 }
 
-export type AppMenuItem = { children: AppMenuItem[] } & UserMenuItem;
+export type AppMenuItem = {
+  id: number;
+  name: string;
+  url: string;
+  type: string;
+  parentId: number;
+  sequence: number;
+  enable: boolean;
+  children: AppMenuItem[];
+};
 
 export interface AppState {
   version: string;
   appMenus: AppMenuItem[];
-  privilegeList: PrivilegeItem[];
   validRoutes: string[];
   breadcrumbs: string[];
+  device: string;
 }

+ 1 - 1
src/store/modules/user/index.ts

@@ -8,7 +8,7 @@ const useUserStore = defineStore('user', {
     id: null,
     account: '',
     name: '',
-    role: '',
+    role: null,
     orgId: null,
     categoryId: null,
     applyTaskId: null,

+ 3 - 3
src/store/modules/user/types.ts

@@ -1,11 +1,11 @@
 import type { RoleType } from '@/constants/enumerate';
 
 export interface UserState {
-  id: number;
+  id: number | null;
   account: string;
   name: string;
-  role: RoleType;
-  orgId: number;
+  role: RoleType | null;
+  orgId: number | null;
   categoryId: number | null;
   applyTaskId: number | null;
   openId: number | null;

+ 15 - 11
src/utils/download.ts

@@ -1,3 +1,4 @@
+import { AxiosResponse } from 'axios';
 import { objTypeOf, blobToText } from './utils';
 
 const parseDownloadFilename = (dispositionInfo: string): string => {
@@ -16,8 +17,8 @@ export function downloadByUrl(url: string, filename?: string): void {
   const tempLink = document.createElement('a');
   tempLink.style.display = 'none';
   tempLink.href = url;
-  const fileName = filename || url.split('/').pop().split('?')[0];
-  tempLink.setAttribute('download', fileName);
+  const fileName = filename || url.split('/').pop()?.split('?')[0];
+  tempLink.setAttribute('download', fileName || '');
   if (tempLink.download === 'undefined') {
     tempLink.setAttribute('target', '_blank');
   }
@@ -37,9 +38,7 @@ export function downloadByBlob(data: Blob, filename: string): void {
   downloadByUrl(blobUrl, filename);
 }
 
-interface ApiFunc {
-  (): Promise<Blob>;
-}
+type ApiFunc = () => Promise<AxiosResponse<Blob>>;
 
 /**
  * 通过api下载文件
@@ -58,15 +57,16 @@ export async function downloadByApi<T extends ApiFunc>(
 
   // 展示后台错误信息
   if (errorInfo && objTypeOf(errorInfo) === 'blob') {
-    const textRes = await blobToText(errorInfo).catch(() => false);
+    const textRes = await blobToText(errorInfo as Blob).catch(() => false);
     if (!textRes) return Promise.reject(new Error('下载失败!'));
-    const resJson = JSON.parse(textRes);
+    const resJson: { message: string } = JSON.parse(textRes as string);
     return Promise.reject(resJson.message);
   }
 
+  const result = res as AxiosResponse<Blob>;
   const filename =
-    fileName || parseDownloadFilename(res.headers['content-disposition']);
-  downloadByBlob(new Blob([res.data]), filename);
+    fileName || parseDownloadFilename(result.headers['content-disposition']);
+  downloadByBlob(new Blob([result.data]), filename);
   return true;
 }
 
@@ -97,8 +97,12 @@ export function downloadByImgUrl(
       canvas.height = img.height;
       ctx.drawImage(img, 0, 0);
       canvas.toBlob((blob) => {
-        downloadByBlob(blob, filename);
-        resolve(true);
+        if (blob) {
+          downloadByBlob(blob, filename);
+          resolve(true);
+          return;
+        }
+        reject(new Error('blob empty'));
       });
     };
     img.onerror = (e) => {

+ 5 - 1
src/utils/md5.ts

@@ -13,7 +13,11 @@ export const fileMD5 = (file: File): Promise<string> => {
     const reader = new FileReader();
     reader.onloadend = () => {
       const arrayBuffer = reader.result;
-      resolve(md5(arrayBuffer));
+      if (arrayBuffer) {
+        resolve(MD5(arrayBuffer));
+      } else {
+        resolve('');
+      }
     };
     reader.onerror = (err) => {
       reject(err);

+ 40 - 36
src/utils/utils.ts

@@ -3,20 +3,13 @@
  * @param {*} obj 对象
  */
 export function objTypeOf(obj: any): string {
-  const map = {
-    '[object Boolean]': 'boolean',
-    '[object Number]': 'number',
-    '[object String]': 'string',
-    '[object Function]': 'function',
-    '[object Array]': 'array',
-    '[object Date]': 'date',
-    '[object RegExp]': 'regExp',
-    '[object Undefined]': 'undefined',
-    '[object Null]': 'null',
-    '[object Object]': 'object',
-    '[object Blob]': 'blob',
-  };
-  return map[Object.prototype.toString.call(obj)];
+  if (obj === null) return 'null';
+  if (Array.isArray(obj)) return 'array';
+  if (obj instanceof Date) return 'date';
+  if (obj instanceof RegExp) return 'regExp';
+  if (obj instanceof Blob) return 'blob';
+  if (typeof obj === 'object') return 'object';
+  return typeof obj;
 }
 
 /**
@@ -24,25 +17,32 @@ export function objTypeOf(obj: any): string {
  * @param {Object} target 目标对象
  * @param {Object} sources 源对象
  */
-export function objAssign<T extends object>(target: T, sources: object): T {
-  const targ = { ...target };
-  Object.keys(targ).forEach((k) => {
-    targ[k] = Object.prototype.hasOwnProperty.call(sources, k)
-      ? sources[k]
-      : targ[k];
+export function objAssign<T extends Record<string, any>>(
+  target: T,
+  sources: Partial<T>
+): T {
+  const merged = {} as T;
+  Object.keys(target).forEach((key) => {
+    const propKey = key as keyof T;
+    merged[propKey] = Object.prototype.hasOwnProperty.call(sources, propKey)
+      ? (sources[propKey] as T[keyof T])
+      : target[propKey];
   });
 
-  return targ;
+  return merged;
 }
 /**
  * 使用目标对象中有的属性值修改源对象中的属性值
  * @param {Object} target 目标对象
  * @param {Object} sources 源对象
  */
-export function objModifyAssign(target: object, sources: object): void {
-  Object.keys(target).forEach((k) => {
+export function objModifyAssign<T extends Record<string, any>>(
+  target: T,
+  sources: Partial<T>
+): void {
+  Object.keys(target).forEach((k: keyof T) => {
     if (Object.prototype.hasOwnProperty.call(sources, k)) {
-      target[k] = sources[k];
+      target[k] = sources[k] as T[keyof T];
     }
   });
 }
@@ -276,13 +276,13 @@ export function humpToLowLine(a: string) {
     .slice(1);
 }
 
-export function pickByNotNull(params: Record<string, any>) {
-  const nData = {};
-  (Object.entries(params) as [string, any][]).forEach(([key, val]) => {
-    if (val === null || val === 'null' || val === '') return;
-    nData[key] = val;
+export function pickByNotNull<T extends Record<string, unknown>>(params: T) {
+  const nData: Partial<T> = {};
+  Object.entries(params).forEach(([key, val]) => {
+    if (val !== null && val !== 'null' && val !== '')
+      nData[key as keyof T] = val as T[keyof T];
   });
-  return nData;
+  return nData as T;
 }
 
 export function autoSubmitForm(url: string, params: Record<string, any>) {
@@ -290,11 +290,11 @@ export function autoSubmitForm(url: string, params: Record<string, any>) {
   form.action = url;
   form.method = 'post';
 
-  (Object.entries(params) as [string, any][]).forEach(([key, val]) => {
+  Object.entries(params).forEach(([key, val]) => {
     const input = document.createElement('input');
     input.type = 'hidden';
     input.name = key;
-    input.value = val;
+    input.value = String(val);
     form.appendChild(input);
   });
   document.body.appendChild(form);
@@ -317,7 +317,7 @@ export function blobToText(blob: Blob): Promise<string | ArrayBuffer | null> {
 export function parseHrefParam(
   urlStr: string,
   paramName = ''
-): Record<string, any> | null {
+): Record<string, string | string[]> | string | null {
   if (!urlStr) return null;
   const url = new URL(urlStr);
 
@@ -325,9 +325,13 @@ export function parseHrefParam(
 
   if (paramName) return urlParams.get(paramName as string);
 
-  const params = {};
-  (urlParams.entries() as [string, string][]).forEach(([k, v]) => {
-    params[k] = v;
+  const params: Record<string, string | string[]> = {};
+  urlParams.forEach((value, key) => {
+    if (params[key]) {
+      params[key] = [...params[key], value];
+    } else {
+      params[key] = value;
+    }
   });
   return params;
 }

+ 1 - 1
src/views/login/login/ResetPwd.vue

@@ -114,7 +114,7 @@
 
     setLoading(true);
     const datas = {
-      id: userStore.id,
+      id: userStore.id as number,
       oldPassword: getBase64(formData.oldPassword),
       password: getBase64(formData.password),
     };

+ 0 - 1
src/views/order/task-manage/ruleForm.vue

@@ -168,7 +168,6 @@
     setLoading(false);
     if (!res) return;
     Message.success('修改成功!');
-    objModifyAssign(props.rowData, formData);
     emit('modified');
   }
   /* init modal */