Browse Source

预约明细页面

zhangjie 1 year ago
parent
commit
041ed92f8d

+ 1 - 1
.env.development

@@ -1 +1 @@
-VUE_APP_DEV_PROXY= 'http://localhost:8080'
+VUE_APP_DEV_PROXY='http://192.168.10.41:8080'

+ 7 - 0
develop.md

@@ -0,0 +1,7 @@
+## 开发服
+
+- 地址:http://192.168.10.41:8080
+- 文档:http://192.168.10.41:8080/doc.html
+- 账号:
+  - 学校管理员:admin/123456
+  - 教学点管理员 gzkd/123456

+ 4 - 32
src/api/interceptor.ts

@@ -7,12 +7,6 @@ import { DEVICE_ID, PLATFORM } from '../constants/app';
 import { objTypeOf } from '../utils/utils';
 import { useUserStore } from '../store';
 
-export interface HttpResponse<T = unknown> {
-  message: string;
-  code: number;
-  data: T;
-}
-
 axios.defaults.timeout = 10 * 1000;
 
 let load = null;
@@ -23,7 +17,7 @@ let unauthMsgBoxIsShow = false;
 
 const modifyConfig = (config: AxiosRequestConfig): void => {
   const userStore = useUserStore();
-  if (userStore.accessToken) {
+  if (userStore.token) {
     const ids = {
       userId: userStore.id,
     };
@@ -36,7 +30,7 @@ const modifyConfig = (config: AxiosRequestConfig): void => {
     const timestamp = fetchTime();
     const Authorization = getAuthorization(
       {
-        token: userStore.accessToken,
+        token: userStore.token,
         timestamp,
         account: userStore.sessionId,
         uri: config.url.split('?')[0],
@@ -82,7 +76,7 @@ axios.interceptors.request.use(
 );
 // add response interceptors
 axios.interceptors.response.use(
-  (response: AxiosResponse<HttpResponse>) => {
+  (response: AxiosResponse) => {
     initSyncTime(new Date(response.headers.date).getTime());
     // 关闭loading提示
     setTimeout(() => {
@@ -92,29 +86,7 @@ axios.interceptors.response.use(
 
     if (response.config.responseType === 'blob') return response;
 
-    const res = response.data;
-    if (res.code !== 200) {
-      Message.error({
-        content: res.message || '响应数据错误',
-        duration: 5 * 1000,
-      });
-      return Promise.reject(res.data);
-    }
-
-    if (
-      objTypeOf(res.data) === 'object' &&
-      Object.prototype.hasOwnProperty.call(res.data, 'success')
-    ) {
-      if (res.data.success) return res.data;
-
-      Message.error({
-        content: res.message || '响应数据错误',
-        duration: 5 * 1000,
-      });
-      return Promise.reject(res.data);
-    }
-
-    return res.data;
+    return response.data;
   },
   (error) => {
     // 关闭loading提示

+ 18 - 18
src/api/order.ts

@@ -10,12 +10,15 @@ import type {
 import { AbleParams } from './types/common';
 
 // common
+export function taskQuery(): Promise<OptionItem[]> {
+  return axios.post('/api/apply/task/list', {});
+}
 export function teachingQuery(): Promise<OptionItem[]> {
-  return axios.post('/api/admin/apply/teaching/list', {});
+  return axios.post('/api/apply/teaching/list', {});
 }
-export function agentQuery(teachingId: number): Promise<OptionItem[]> {
+export function agentQuery(teachingId: string): Promise<OptionItem[]> {
   return axios.post(
-    '/api/admin/apply/agent/list',
+    '/api/apply/agent/list',
     {},
     { params: { id: teachingId } }
   );
@@ -44,37 +47,34 @@ export function ableTask(params: AbleParams): Promise<boolean> {
 export function orderRecordListPage(
   params: OrderRecordListPageParam
 ): Promise<OrderRecordListPageRes> {
-  return axios.post('/api/admin/apply/std/query', {}, { params });
+  return axios.post('/api/apply/std/page', params);
 }
 // 预约名单详情-取消预约
-export function orderRecordCancel(apptId: number): Promise<boolean> {
-  return axios.post('/api/admin/appt/std/cancel', {}, { params: { apptId } });
+export function orderRecordCancel(id: number): Promise<boolean> {
+  return axios.post('/api/apply/std/cancel', {}, { params: { id } });
 }
 // 预约名单详情-一键自动分配
-export function orderRecordAutoAssign(): Promise<boolean> {
-  return axios.post('/api/admin/apply/std/auto/assign', {});
+export function orderRecordAutoAssign(params: {
+  taskId?: number;
+}): Promise<boolean> {
+  return axios.post('/api/apply/std/auto/assign', {}, { params });
 }
-// 预约名单详情-导出
-export function orderRecordExport(params: {
-  teachingId: number;
-  agentId: number;
-}): Promise<Blob> {
+// 预约名单详情-导入模板下载
+export function orderRecordTemplate(): Promise<Blob> {
   return axios.post(
-    '/api/admin/apply/std/export',
+    '/api/apply/imp/template',
     {},
     {
       responseType: 'blob',
-      params,
     }
   );
 }
 // 预约名单详情-打印签到表
 export function orderRecordPrint(params: {
-  teachingId: number;
-  agentId: number;
+  teachingId?: number;
 }): Promise<Blob> {
   return axios.post(
-    '/api/admin/apply/std/sign/in/print',
+    '/api/apply/std/auto/sign/in/print',
     {},
     {
       responseType: 'blob',

+ 5 - 4
src/api/types/common.ts

@@ -1,8 +1,9 @@
 export interface PageResult<T = unknown> {
-  size: number;
-  total: number;
-  pages: number;
-  records: Array<T>;
+  pageSize: number;
+  pageNumber: number;
+  totalCount: number;
+  pageCount: number;
+  result: Array<T>;
 }
 export type PageParams<T = unknown> = T & {
   pageNumber: number;

+ 12 - 8
src/api/types/order.ts

@@ -1,7 +1,7 @@
 import { PageResult, PageParams } from './common';
 
 export interface OptionItem {
-  id: string;
+  id: number;
   name: string;
 }
 
@@ -50,18 +50,22 @@ export interface TaskRuleUpdateParams {
 
 export interface OrderRecordItem {
   id: string;
-  stdName: string;
+  name: string;
   identityNumber: string;
   studentCode: string;
-  teachingName: string;
-  agentName: string; //未预约的时候为空
-  applyTimePeriod: string; //未预约的时候为空
-  roomName: string; //未排考为空
-  seatNumber: string; //未排考为空
-  operationTime: string; //未预约的时候为空
+  teachingName: string; // 教学点
+  agentName: string; // 考点
+  roomName: string; //考场
+  seatNumber: string; //座位号
+  cancel: boolean; // 是否取消
+  endTime: number;
+  startTime: number;
+  updateTime: number; // 操作时间
+  userName: string; // 操作人
 }
 export type OrderRecordListPageRes = PageResult<OrderRecordItem>;
 export interface OrderRecordListFilter {
+  taskId: number;
   teachingId: number;
   agentId: number;
   name: string;

+ 1 - 15
src/api/types/user.ts

@@ -1,5 +1,5 @@
 export interface LoginData {
-  loginName: string;
+  account: string;
   password: string;
 }
 
@@ -8,17 +8,3 @@ export interface UpdatePwdData {
   oldPassword: string;
   password: string;
 }
-
-export interface UserMenuItem {
-  id: string;
-  name: string;
-  url: string;
-  type: string;
-  parentId: string;
-  sequence: number;
-  enable: boolean;
-}
-export interface UserMenuRes {
-  userId: string;
-  privileges: UserMenuItem[];
-}

+ 3 - 7
src/api/user.ts

@@ -1,9 +1,9 @@
 import axios from 'axios';
 import { UserState } from '@/store/modules/user/types';
-import type { LoginData, UpdatePwdData, UserMenuRes } from './types/user';
+import type { LoginData, UpdatePwdData } from './types/user';
 
 export function login(data: LoginData): Promise<UserState> {
-  return axios.post('/api/admin/sys/user/login', data);
+  return axios.post('/api/user/login', data);
 }
 
 export function updatePwd(datas: UpdatePwdData): Promise<UserState> {
@@ -11,9 +11,5 @@ export function updatePwd(datas: UpdatePwdData): Promise<UserState> {
 }
 
 export function userLogout() {
-  return axios.post('/api/admin/sys/user/logout', {});
-}
-
-export function sysMenu(): Promise<UserMenuRes> {
-  return axios.post('/api/admin/sys/user/get_menu', {});
+  return axios.post('/api/user/logout', {});
 }

+ 9 - 0
src/assets/style/base.less

@@ -28,6 +28,15 @@
     .a-form-item__label {
       display: none;
     }
+    .arco-select {
+      width: 200px;
+    }
+    .arco-input-wrapper {
+      width: 200px;
+    }
+    .arco-form-item-layout-inline {
+      margin-right: 16px;
+    }
   }
 
   &-action {

+ 2 - 0
src/components/index.ts

@@ -1,12 +1,14 @@
 import { App } from 'vue';
 
 // selection
+import SelectTask from './select-task/index.vue';
 import SelectTeaching from './select-teaching/index.vue';
 import SelectAgent from './select-agent/index.vue';
 import SelectRangeDatetime from './select-range-datetime/index.vue';
 
 export default {
   install(Vue: App) {
+    Vue.component('SelectTask', SelectTask);
     Vue.component('SelectTeaching', SelectTeaching);
     Vue.component('SelectAgent', SelectAgent);
     Vue.component('SelectRangeDatetime', SelectRangeDatetime);

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

@@ -21,18 +21,18 @@
   });
 
   const props = defineProps<{
-    modelValue: string;
+    modelValue: number | string;
     clearable?: boolean;
     disabled?: boolean;
     placeholder?: string;
     multiple?: boolean;
-    teachingId: number;
+    teachingId: number | string;
   }>();
   const emit = defineEmits(['update:modelValue', 'change']);
   const attrs = useAttrs();
 
   interface OptionListItem {
-    value: string;
+    value: number;
     label: string;
   }
 
@@ -71,6 +71,8 @@
   watch(
     () => props.teachingId,
     () => {
+      emit('update:modelValue', '');
+      emit('change', null);
       search();
     },
     {

+ 69 - 0
src/components/select-task/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <a-select
+    v-model="selected"
+    :placeholder="placeholder"
+    :allow-clear="clearable"
+    :disabled="disabled"
+    :options="optionList"
+    filter-option
+    v-bind="attrs"
+    @change="onChange"
+  >
+  </a-select>
+</template>
+
+<script setup lang="ts">
+  import { ref, useAttrs, watch } from 'vue';
+  import { taskQuery } from '@/api/order';
+
+  defineOptions({
+    name: 'SelectTask',
+  });
+
+  const props = defineProps<{
+    modelValue: number | string;
+    clearable?: boolean;
+    disabled?: boolean;
+    placeholder?: string;
+    multiple?: boolean;
+  }>();
+  const emit = defineEmits(['update:modelValue', 'change']);
+  const attrs = useAttrs();
+
+  interface OptionListItem {
+    value: number;
+    label: string;
+  }
+
+  const selected = ref('');
+  const optionList = ref<OptionListItem[]>([]);
+  const search = async () => {
+    optionList.value = [];
+    const resData = await taskQuery();
+
+    optionList.value = (resData || []).map((item) => {
+      return { ...item, value: item.id, label: item.name };
+    });
+  };
+  search();
+
+  const onChange = () => {
+    const selectedData = props.multiple
+      ? optionList.value.filter(
+          (item) => selected.value && selected.value.includes(item.value)
+        )
+      : optionList.value.filter((item) => selected.value === item.value);
+    emit('update:modelValue', selected.value);
+    emit('change', props.multiple ? selectedData : selectedData[0]);
+  };
+
+  watch(
+    () => props.modelValue,
+    (val) => {
+      selected.value = val;
+    },
+    {
+      immediate: true,
+    }
+  );
+</script>

+ 2 - 2
src/components/select-teaching/index.vue

@@ -21,7 +21,7 @@
   });
 
   const props = defineProps<{
-    modelValue: string;
+    modelValue: number | string;
     clearable?: boolean;
     disabled?: boolean;
     placeholder?: string;
@@ -31,7 +31,7 @@
   const attrs = useAttrs();
 
   interface OptionListItem {
-    value: string;
+    value: number;
     label: string;
   }
 

+ 4 - 6
src/constants/enumerate.ts

@@ -12,10 +12,8 @@ export const ABLE_TYPE = {
 // 基础 -------------->
 // 角色
 export const ROLE_TYPE = {
-  SCHOOL_ADMIN: '管理员',
-  EXAM_TEACHER: '考务老师',
-  QUESTION_TEACHER: '命题老师',
-  CUSTOMER: '客服人员',
-  PRINT: '印刷人员',
-  CUSTOM: '自定义',
+  ADMIN: '学校管理员',
+  EXAM_TEACHER: '教学点管理员',
 };
+
+export type RoleType = keyof typeof ROLE_TYPE;

+ 2 - 3
src/hooks/request.ts

@@ -1,6 +1,5 @@
 import { ref, UnwrapRef } from 'vue';
 import { AxiosResponse } from 'axios';
-import { HttpResponse } from '@/api/interceptor';
 import useLoading from './loading';
 
 // use to fetch list
@@ -9,7 +8,7 @@ import useLoading from './loading';
 // example: useRequest(api.bind(null, {}))
 
 export default function useRequest<T>(
-  api: () => Promise<AxiosResponse<HttpResponse>>,
+  api: () => Promise<AxiosResponse>,
   defaultValue = [] as unknown as T,
   isLoading = true
 ) {
@@ -17,7 +16,7 @@ export default function useRequest<T>(
   const response = ref<T>(defaultValue);
   api()
     .then((res) => {
-      response.value = res.data as unknown as UnwrapRef<T>;
+      response.value = res as unknown as UnwrapRef<T>;
     })
     .finally(() => {
       setLoading(false);

+ 2 - 2
src/hooks/table.ts

@@ -18,8 +18,8 @@ export default function useTable<T>(
       pageSize: pageSize.value,
     };
     const data = await apiFunc(datas);
-    dataList.value = data.records;
-    total.value = data.total;
+    dataList.value = data.result;
+    total.value = data.totalCount;
   }
   if (initAutoFetch) getList();
 

+ 1 - 1
src/main.ts

@@ -4,7 +4,7 @@ import ArcoVueIcon from '@arco-design/web-vue/es/icon';
 import globalComponents from '@/components';
 import router from './router';
 import store from './store';
-import './mock';
+// import './mock';
 import App from './App.vue';
 // Styles are imported via arco-plugin. See config/plugin/arcoStyleImport.ts in the directory for details
 // 样式通过 arco-plugin 插件导入。详见目录文件 config/plugin/arcoStyleImport.ts

+ 31 - 1
src/mock/task.ts

@@ -1,10 +1,40 @@
 import Mock from 'mockjs';
-import setupMock, { pageListResponseWrap } from '@/utils/setup-mock';
+import setupMock, {
+  pageListResponseWrap,
+  successResponseWrap,
+} from '@/utils/setup-mock';
 
 setupMock({
   setup() {
     // Mock.XHR.prototype.withCredentials = true;
 
+    // 教学点列表
+    Mock.mock(new RegExp('/api/admin/apply/teaching/list'), () => {
+      return successResponseWrap([
+        {
+          id: '111',
+          name: '教学点11',
+        },
+        {
+          id: '222',
+          name: '教学点12',
+        },
+      ]);
+    });
+    // 考点列表
+    Mock.mock(new RegExp('/api/admin/apply/agent/list'), () => {
+      return successResponseWrap([
+        {
+          id: '111',
+          name: '考点11',
+        },
+        {
+          id: '222',
+          name: '考点12',
+        },
+      ]);
+    });
+
     // 预约任务查询
     Mock.mock(new RegExp('/api/admin/apply/task/page'), () => {
       return pageListResponseWrap([

+ 4 - 3
src/router/guard/permission.ts

@@ -1,11 +1,11 @@
 import type { Router } from 'vue-router';
 import NProgress from 'nprogress'; // progress bar
 
-import { useAppStore } from '@/store';
+import { useAppStore, useUserStore } from '@/store';
 import { WHITE_LIST, NOT_FOUND, DEFAULT_ROUTE_NAME } from '../constants';
 
 export default function setupUserLoginInfoGuard(router: Router) {
-  router.beforeEach(async (to, from, next) => {
+  router.beforeEach((to, from, next) => {
     if (WHITE_LIST.find((el) => el.name === to.name)) {
       next();
       NProgress.done();
@@ -14,7 +14,8 @@ export default function setupUserLoginInfoGuard(router: Router) {
 
     const appStore = useAppStore();
     if (!appStore.appMenus.length) {
-      await appStore.fetchServerMenu();
+      const useStore = useUserStore();
+      appStore.fetchServerMenu(useStore.role);
     }
 
     if (to.name === DEFAULT_ROUTE_NAME) {

+ 4 - 6
src/router/guard/userLoginInfo.ts

@@ -11,14 +11,12 @@ export default function setupUserLoginInfoGuard(router: Router) {
       next();
       return;
     }
-    if (userStore.accessToken) {
+    if (userStore.token) {
       next();
     } else {
-      // TODO:开发放开
-      next();
-      // next({
-      //   name: 'Login',
-      // });
+      next({
+        name: 'Login',
+      });
     }
   });
 }

+ 1 - 1
src/router/routes/modules/login.ts

@@ -5,7 +5,7 @@ const LOGIN: AppRouteRecordRaw = {
   component: () => import('@/views/login/home.vue'),
   children: [
     {
-      path: '/login/:code?',
+      path: '/login',
       name: 'Login',
       component: () => import('@/views/login/login/index.vue'),
       meta: {

+ 12 - 20
src/store/modules/app/index.ts

@@ -1,22 +1,15 @@
 import { defineStore } from 'pinia';
-import { sysMenu } from '@/api/user';
+import { omit } from 'lodash';
+import { menus } from './menuData';
 
-import type { UserMenuItem } from '@/api/types/user';
-import { AppState, PrivilegeItem, AppMenuItem } from './types';
+import { AppState, AppMenuItem, UserMenuItem } from './types';
+import { RoleType } from '../../../constants/enumerate';
 
-function transformMenu(list: UserMenuItem[]): PrivilegeItem[] {
-  return list.map((item) => {
-    return {
-      id: item.id,
-      parentId: item.parentId,
-      name: item.name,
-      type: item.type,
-      url: item.url,
-    };
-  });
+function transformMenu(list: typeof menus): UserMenuItem[] {
+  return list.map((menu) => omit(menu, ['roles']));
 }
 
-function getMenu(privilegeData: PrivilegeItem[]): {
+function getMenu(privilegeData: UserMenuItem[]): {
   menuList: AppMenuItem[];
   validRoutes: string[];
 } {
@@ -67,12 +60,11 @@ const useAppStore = defineStore('app', {
     setInfo(partial: Partial<AppState>) {
       this.$patch(partial);
     },
-    async fetchServerMenu() {
-      const res = await sysMenu().catch(() => false);
-      if (!res) return;
-      const privilegeList: UserMenuItem[] = [...res.privileges];
-      this.privilegeList = transformMenu(privilegeList);
-      const { menuList, validRoutes } = getMenu(this.privilegeList);
+    fetchServerMenu(type: RoleType) {
+      const userMenus = menus.filter(
+        (menu) => !menu.roles || menu.roles.includes(type)
+      );
+      const { menuList, validRoutes } = getMenu(transformMenu(userMenus));
       this.appMenus = menuList;
       this.validRoutes = validRoutes;
     },

+ 41 - 0
src/store/modules/app/menuData.ts

@@ -0,0 +1,41 @@
+export const menus = [
+  {
+    id: '1',
+    name: '考试预约管理',
+    url: 'base',
+    type: 'MENU',
+    parentId: '-1',
+    sequence: 1,
+    enable: true,
+  },
+  {
+    id: '2',
+    name: '预约任务管理',
+    url: 'TaskManage',
+    type: 'MENU',
+    parentId: '1',
+    sequence: 1,
+    enable: true,
+    roles: [],
+  },
+  {
+    id: '3',
+    name: '考生信息导入',
+    url: 'StudentManage',
+    type: 'MENU',
+    parentId: '1',
+    sequence: 2,
+    enable: true,
+    roles: [],
+  },
+  {
+    id: '4',
+    name: '预约名单详情',
+    url: 'OrderRecordManage',
+    type: 'MENU',
+    parentId: '1',
+    sequence: 3,
+    enable: true,
+    roles: ['ADMIN'],
+  },
+];

+ 9 - 1
src/store/modules/app/types.ts

@@ -1,4 +1,12 @@
-import type { UserMenuItem } from '@/api/types/user';
+export interface UserMenuItem {
+  id: string;
+  name: string;
+  url: string;
+  type: string;
+  parentId: string;
+  sequence: number;
+  enable: boolean;
+}
 
 export type AppMenuItem = { children: AppMenuItem[] } & UserMenuItem;
 

+ 10 - 9
src/store/modules/user/index.ts

@@ -5,15 +5,16 @@ import { UserState } from './types';
 
 const useUserStore = defineStore('user', {
   state: (): UserState => ({
-    id: undefined,
-    loginName: undefined,
-    name: undefined,
-    sessionId: undefined,
-    accessToken: undefined,
-    categoryInfo: {
-      name: undefined,
-      categoryId: undefined,
-    },
+    id: null,
+    account: '',
+    name: '',
+    role: '',
+    orgId: null,
+    categoryId: null,
+    applyTaskId: null,
+    openId: null,
+    sessionId: '',
+    token: '',
   }),
 
   getters: {

+ 10 - 7
src/store/modules/user/types.ts

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

+ 1 - 1
src/utils/download.ts

@@ -3,7 +3,7 @@ import { objTypeOf, blobToText, objAssign } from './utils';
 
 const parseDownloadFilename = (dispositionInfo: string): string => {
   if (!dispositionInfo) return '';
-  const matchs = dispositionInfo.match(/;filename=(.*)/) || [];
+  const matchs = dispositionInfo.match(/filename=(.*)/) || [];
   const filename = matchs[1];
   return filename ? decodeURI(filename) : '';
 };

+ 5 - 0
src/utils/utils.ts

@@ -270,6 +270,11 @@ export function parseTimeRangeDateAndTime(startTime, endTime) {
   };
 }
 
+export function parseTimeRangeDateAndTimeContent(startTime, endTime) {
+  const { date, time } = parseTimeRangeDateAndTime(startTime, endTime);
+  return `${date} ${time}`;
+}
+
 /**
  * 获取本地时间,格式:年月日时分秒
  */

+ 5 - 8
src/views/login/login/index.vue

@@ -13,9 +13,9 @@
           :label-col-props="{ span: 0, offset: 0 }"
           :wrapper-col-props="{ span: 24, offset: 0 }"
         >
-          <a-form-item field="loginName">
+          <a-form-item field="account">
             <a-input
-              v-model.trim="formData.loginName"
+              v-model.trim="formData.account"
               placeholder="请输入账号"
               allow-clear
             >
@@ -63,11 +63,11 @@
 
   const formRef = ref<FormInstance>();
   const formData = reactive({
-    loginName: 'admin-zj',
-    password: 'a12345678',
+    account: 'admin',
+    password: '123456',
   });
   const rules: Record<string, FieldRule[]> = {
-    loginName: [
+    account: [
       {
         required: true,
         message: '请输入用户名',
@@ -92,10 +92,7 @@
     setLoading(false);
     if (!data) return;
 
-    console.log(data);
-
     userStore.setInfo(data);
-
     router.push({
       name: DEFAULT_ROUTE_NAME,
     });

+ 61 - 38
src/views/order/order-record-manage/index.vue

@@ -6,6 +6,13 @@
       :label-col-props="{ span: 0, offset: 0 }"
       :wrapper-col-props="{ span: 24, offset: 0 }"
     >
+      <a-form-item label="教学点">
+        <SelectTask
+          v-model="searchModel.taskId"
+          placeholder="教学点"
+          allow-clear
+        />
+      </a-form-item>
       <a-form-item label="教学点">
         <SelectTeaching
           v-model="searchModel.teachingId"
@@ -47,9 +54,6 @@
       </a-form-item>
     </a-form>
     <div>
-      <a-button type="primary" :loading="exportLoading" @click="toExport"
-        >导出</a-button
-      >
       <a-button type="primary" @click="toImport">导入预考</a-button>
       <a-button type="primary" :loading="assginLoading" @click="toAssgin"
         >一键自动分配</a-button
@@ -61,11 +65,21 @@
   </div>
   <div class="part-box">
     <a-table :columns="columns" :data="dataList" :pagination="pagination">
-      <template #operationTime="{ record }">
-        {{ timestampFilter(record.operationTime) }}
+      <template #status="{ record }">
+        {{ record.cancel ? '已取消' : '正常' }}
+      </template>
+      <template #period="{ record }">
+        {{ parseTimeRangeDateAndTimeContent(record.startTime, record.endTime) }}
+      </template>
+      <template #updateTime="{ record }">
+        {{ timestampFilter(record.updateTime) }}
       </template>
       <template #action="{ record }">
-        <a-button type="text" class="btn-primary" @click="toCancel(record)"
+        <a-button
+          v-if="!record.cancel"
+          type="text"
+          class="btn-danger"
+          @click="toCancel(record)"
           >取消</a-button
         >
       </template>
@@ -73,12 +87,13 @@
 
     <!-- ImportDialog -->
     <ImportDialog
-      ref="importStudentDialog"
+      ref="importStudentDialogRef"
       title="导入预考"
-      upload-url="/api/admin/sys/user/import"
+      upload-url="/api/apply/import"
+      :upload-data="uploadData"
       :format="['xls', 'xlsx']"
-      download-url="12312"
-      download-filename="学生预约导入模板.xlsx"
+      :download-handle="downloadTemplate"
+      download-filename="预考导入模板.xlsx"
       :auto-upload="false"
       @upload-success="getList"
     />
@@ -86,14 +101,14 @@
 </template>
 
 <script setup lang="ts">
-  import { reactive, ref } from 'vue';
+  import { computed, reactive, ref } from 'vue';
   import { Message, TableColumnData } from '@arco-design/web-vue';
   import {
     orderRecordCancel,
     orderRecordAutoAssign,
     orderRecordListPage,
     orderRecordPrint,
-    orderRecordExport,
+    orderRecordTemplate,
   } from '@/api/order';
   import { OrderRecordItem } from '@/api/types/order';
   import useTable from '@/hooks/table';
@@ -102,6 +117,7 @@
   import { timestampFilter } from '@/utils/filter';
   import { modalConfirm } from '@/utils/arco';
   import { downloadByApi } from '@/utils/download';
+  import { parseTimeRangeDateAndTimeContent } from '@/utils/utils';
 
   import ImportDialog from '@/components/import-dialog/index.vue';
 
@@ -112,6 +128,7 @@
   // const { getLabel: getAbleLabel } = useDictOption('ABLE_TYPE');
 
   const searchModel = reactive({
+    taskId: '',
     teachingId: '',
     agentId: '',
     name: '',
@@ -122,87 +139,92 @@
   const columns: TableColumnData[] = [
     {
       title: '姓名',
-      dataIndex: 'stdName',
+      dataIndex: 'name',
+      width: 120,
     },
     {
       title: '证件号',
       dataIndex: 'identityNumber',
+      width: 200,
     },
     {
       title: '学号',
       dataIndex: 'studentCode',
+      width: 160,
     },
     {
       title: '教学点',
       dataIndex: 'teachingName',
+      width: 160,
     },
     {
       title: '考点',
       dataIndex: 'agentName',
+      width: 160,
     },
     {
       title: '预约时段',
-      dataIndex: 'applyTimePeriod',
+      dataIndex: 'startTime',
+      slotName: 'period',
       width: 200,
     },
     {
       title: '考场',
       dataIndex: 'roomName',
+      width: 120,
     },
     {
       title: '座位号',
       dataIndex: 'seatNumber',
+      width: 100,
     },
     {
       title: '状态',
-      dataIndex: 'seatNumber',
+      dataIndex: 'cancel',
+      slotName: 'status',
+      width: 100,
     },
     {
       title: '操作时间',
-      dataIndex: 'operationTime',
-      slotName: 'operationTime',
+      dataIndex: 'updateTime',
+      slotName: 'updateTime',
       width: 170,
     },
     {
       title: '操作人',
-      dataIndex: 'operationUser',
+      dataIndex: 'userName',
+      width: 120,
     },
     {
       title: '操作',
       slotName: 'action',
-      width: 100,
+      width: 80,
       fixed: 'right',
       cellClass: 'action-column',
     },
   ];
+  const uploadData = computed(() => {
+    return searchModel.teachingId ? { teachingId: searchModel.teachingId } : {};
+  });
   const { dataList, pagination, toPage, getList } = useTable<OrderRecordItem[]>(
     orderRecordListPage,
     searchModel,
     true
   );
 
-  // table action
-  const { loading: exportLoading, setLoading: setExportLoading } = useLoading();
-  async function toExport() {
-    if (exportLoading.value) return;
-    setExportLoading(true);
-
-    const res = await downloadByApi(() =>
-      orderRecordExport({
-        teachingId: searchModel.teachingId,
-        agentId: searchModel.agentId,
-      })
-    ).catch((e) => {
+  async function downloadTemplate() {
+    const res = await downloadByApi(() => orderRecordTemplate()).catch((e) => {
       Message.error(e || '下载失败,请重新尝试!');
     });
-    setExportLoading(false);
     if (!res) return;
     Message.success('下载成功!');
   }
+
+  // table action
   // 导入
-  const importStudentDialog = ref(null);
+  const importStudentDialogRef = ref(null);
   function toImport() {
-    importStudentDialog.value?.open();
+    importStudentDialogRef.value?.open();
   }
 
   // 一键分配
@@ -211,7 +233,9 @@
     if (assginLoading.value) return;
     setAssignLoading(true);
 
-    const res = await orderRecordAutoAssign().catch(() => false);
+    const res = await orderRecordAutoAssign({
+      taskId: searchModel.taskId || undefined,
+    }).catch(() => false);
     setAssignLoading(false);
     if (!res) return;
     Message.success('操作成功!');
@@ -226,8 +250,7 @@
 
     const res = await downloadByApi(() =>
       orderRecordPrint({
-        teachingId: searchModel.teachingId,
-        agentId: searchModel.agentId,
+        teachingId: searchModel.teachingId || undefined,
       })
     ).catch((e) => {
       Message.error(e || '下载失败,请重新尝试!');
@@ -241,7 +264,7 @@
   async function toCancel(row: OrderRecordItem) {
     const confirmRes = await modalConfirm(
       '提示',
-      `确定要取消【${row.stdName}】的预约吗?`
+      `确定要取消【${row.name}】的预约吗?`
     ).catch(() => false);
     if (confirmRes !== 'confirm') return;