Browse Source

考场管理

zhangjie 1 năm trước cách đây
mục cha
commit
00d88d82ec

+ 29 - 0
src/api/base.ts

@@ -6,6 +6,9 @@ import type {
   AgentListPageParam,
   AgentListPageRes,
   AgentUpdateParams,
+  RoomListPageParam,
+  RoomListPageRes,
+  RoomUpdateParams,
 } from './types/base';
 import { AbleParams } from './types/common';
 
@@ -62,3 +65,29 @@ export function agentTemplate(): Promise<Blob> {
     }
   );
 }
+
+// 考场管理
+// 考场管理-查询
+export function roomListPage(
+  params: RoomListPageParam
+): Promise<RoomListPageRes> {
+  return axios.post('/api/admin/room/query', {}, { params });
+}
+// 考场管理-新增编辑
+export function updateRoom(datas: RoomUpdateParams): Promise<{ id: string }> {
+  return axios.post('/api/admin/room/save', datas);
+}
+// 考场管理-启用禁用
+export function ableRoom(params: AbleParams): Promise<boolean> {
+  return axios.post('/api/admin/room/enable', {}, { params });
+}
+// // 考场管理-导入模板下载
+export function roomTemplate(): Promise<Blob> {
+  return axios.post(
+    '/api/admin/room/template',
+    {},
+    {
+      responseType: 'blob',
+    }
+  );
+}

+ 34 - 0
src/api/types/base.ts

@@ -58,3 +58,37 @@ export interface AgentUpdateParams {
   guide: string;
   enable?: boolean;
 }
+
+export interface RoomListFilter {
+  teachingId: string;
+  agentId: string;
+  name: string;
+  enable: boolean;
+}
+export type RoomListPageParam = PageParams<RoomListFilter>;
+
+export interface RoomItem {
+  id: number;
+  name: string;
+  code: string;
+  address: string;
+  teachingId: number;
+  teachingName: string;
+  agentId: number;
+  agentName: string;
+  capacity: number;
+  enable: boolean;
+}
+
+export type RoomListPageRes = PageResult<RoomItem>;
+
+export interface RoomUpdateParams {
+  id?: number;
+  name: string;
+  code: string;
+  address: string;
+  capactiy: number;
+  teachingId: number;
+  agentId: number;
+  enable?: boolean;
+}

+ 30 - 0
src/mock/task.ts

@@ -123,5 +123,35 @@ setupMock({
         },
       ]);
     });
+    // 考场管理
+    // 考场管理-查询
+    Mock.mock(new RegExp('/api/admin/room/query'), () => {
+      return pageListResponseWrap([
+        {
+          id: 1,
+          name: '考场01',
+          code: 'kc01',
+          teachingId: 1,
+          teachingName: '教学点01',
+          agentId: 1,
+          agentName: '考点01',
+          address: '武汉市洪山区关山大道',
+          capacity: 150,
+          enable: true,
+        },
+        {
+          id: 2,
+          name: '考场02',
+          code: 'kc02',
+          teachingId: 2,
+          teachingName: '教学点02',
+          agentId: 2,
+          agentName: '考点02',
+          address: '武汉市洪山区关山大道',
+          capacity: 200,
+          enable: true,
+        },
+      ]);
+    });
   },
 });

+ 9 - 0
src/router/routes/modules/base.ts

@@ -27,6 +27,15 @@ const routes: AppRouteRecordRaw = {
         requiresAuth: true,
       },
     },
+    {
+      path: 'room-manage',
+      name: 'RoomManage',
+      component: () => import('@/views/base/room-manage/index.vue'),
+      meta: {
+        title: '考场管理',
+        requiresAuth: true,
+      },
+    },
   ],
 };
 

+ 1 - 1
src/views/base/agent-manage/index.vue

@@ -193,7 +193,7 @@
     const action = row.enable ? '禁用' : '启用';
     const confirmRes = await modalConfirm(
       '提示',
-      `确定要${action}任务【${row.name}】吗?`
+      `确定要${action}考点【${row.name}】吗?`
     ).catch(() => false);
     if (confirmRes !== 'confirm') return;
 

+ 4 - 1
src/views/base/agent-manage/modifyAgent.vue

@@ -60,7 +60,7 @@
 </template>
 
 <script setup lang="ts">
-  import { computed, reactive, ref } from 'vue';
+  import { computed, nextTick, reactive, ref } from 'vue';
   import { Message } from '@arco-design/web-vue';
   import type { FormInstance } from '@arco-design/web-vue/es/form';
   import { updateAgent } from '@/api/base';
@@ -164,5 +164,8 @@
     } else {
       objModifyAssign(formData, defaultFormData);
     }
+    nextTick(() => {
+      formRef.value?.clearValidate();
+    });
   }
 </script>

+ 202 - 0
src/views/base/room-manage/index.vue

@@ -0,0 +1,202 @@
+<template>
+  <div class="part-box is-filter">
+    <a-space class="filter-line" :size="12" wrap>
+      <SelectTeaching
+        v-model="searchModel.teachingId"
+        placeholder="请选择"
+        allow-clear
+        prefix
+      />
+      <SelectAgent
+        v-model="searchModel.agentId"
+        placeholder="请选择"
+        allow-clear
+        prefix
+        :teaching-id="searchModel.teachingId"
+      />
+      <a-input v-model.trim="searchModel.name" placeholder="请输入" allow-clear>
+        <template #prefix>考场名称</template>
+      </a-input>
+      <a-select
+        v-model="searchModel.enable"
+        placeholder="请选择"
+        allow-clear
+        :options="ableOptions"
+      >
+        <template #prefix>状态</template>
+      </a-select>
+
+      <a-button type="primary" @click="toPage(1)">查询</a-button>
+    </a-space>
+  </div>
+  <div class="part-box">
+    <a-space class="part-action" :size="6">
+      <a-button type="text" @click="toAdd">
+        <template #icon>
+          <svg-icon name="icon-add" />
+        </template>
+        新增
+      </a-button>
+      <a-divider direction="vertical" />
+      <a-button type="text" @click="toImport">
+        <template #icon>
+          <svg-icon name="icon-import" />
+        </template>
+        导入
+      </a-button>
+    </a-space>
+    <a-table
+      class="page-table"
+      :columns="columns"
+      :data="dataList"
+      :pagination="pagination"
+      :scroll="{ x: 1200 }"
+      :bordered="false"
+    >
+      <template #enable="{ record }">
+        <status-tag type="enable" :value="record.enable" />
+      </template>
+      <template #action="{ record }">
+        <a-button type="text" class="btn-primary" @click="toEdit(record)"
+          >编辑</a-button
+        >
+        <a-button
+          :class="record.enable ? 'btn-danger' : 'btn-primary'"
+          type="text"
+          @click="toEnable(record)"
+          >{{ record.enable ? '禁用' : '启用' }}</a-button
+        >
+      </template>
+    </a-table>
+
+    <!-- ImportDialog -->
+    <ImportDialog
+      ref="importDialogRef"
+      title="导入考场"
+      upload-url="/api/admin/room/import"
+      :format="['xls', 'xlsx']"
+      :download-handle="downloadTemplate"
+      download-filename="考场导入模板.xlsx"
+      :auto-upload="false"
+      @upload-success="getList"
+    />
+    <!-- ModifyRoom -->
+    <ModifyRoom ref="modifyRoomRef" :row-data="curRow" @modified="getList" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive, ref } from 'vue';
+  import { Message, TableColumnData } from '@arco-design/web-vue';
+  import { roomListPage, roomTemplate, ableRoom } from '@/api/base';
+  import { RoomItem } from '@/api/types/base';
+  import useTable from '@/hooks/table';
+  import useDictOption from '@/hooks/dict-option';
+  import { modalConfirm } from '@/utils/arco';
+  import { downloadByApi } from '@/utils/download';
+  import { useAppStore } from '@/store';
+
+  import ImportDialog from '@/components/import-dialog/index.vue';
+  import ModifyRoom from './modifyRoom.vue';
+
+  defineOptions({
+    name: 'RoomManage',
+  });
+
+  const appStore = useAppStore();
+  appStore.setInfo({ breadcrumbs: ['基础信息管理', '考场管理'] });
+
+  const { optionList: ableOptions } = useDictOption('ABLE_TYPE');
+
+  const searchModel = reactive({
+    teachingId: '',
+    agentId: '',
+    name: '',
+    enable: null,
+  });
+
+  const columns: TableColumnData[] = [
+    {
+      title: '考场代码',
+      dataIndex: 'code',
+      width: 120,
+    },
+    {
+      title: '考场名称',
+      dataIndex: 'name',
+    },
+    {
+      title: '考场地址',
+      dataIndex: 'address',
+    },
+    {
+      title: '机房容量',
+      dataIndex: 'capacity',
+      width: 100,
+    },
+    {
+      title: '所属考点',
+      dataIndex: 'agentName',
+    },
+    {
+      title: '所属教学点',
+      dataIndex: 'teachingName',
+    },
+    {
+      title: '状态',
+      slotName: 'enable',
+      width: 100,
+    },
+    {
+      title: '操作',
+      slotName: 'action',
+      width: 120,
+      fixed: 'right',
+      cellClass: 'action-column',
+    },
+  ];
+  const { dataList, pagination, toPage, getList } = useTable<RoomItem[]>(
+    roomListPage,
+    searchModel,
+    true
+  );
+
+  async function downloadTemplate() {
+    const res = await downloadByApi(() => roomTemplate()).catch((e) => {
+      Message.error(e || '下载失败,请重新尝试!');
+    });
+    if (!res) return;
+    Message.success('下载成功!');
+  }
+
+  // table action
+  // 导入
+  const importDialogRef = ref(null);
+  function toImport() {
+    importDialogRef.value?.open();
+  }
+
+  const modifyRoomRef = ref(null);
+  const curRow = ref({});
+  function toAdd() {
+    curRow.value = {};
+    modifyRoomRef.value?.open();
+  }
+  function toEdit(row: RoomItem) {
+    curRow.value = row;
+    modifyRoomRef.value?.open();
+  }
+
+  async function toEnable(row: RoomItem) {
+    const action = row.enable ? '禁用' : '启用';
+    const confirmRes = await modalConfirm(
+      '提示',
+      `确定要${action}考场【${row.name}】吗?`
+    ).catch(() => false);
+    if (confirmRes !== 'confirm') return;
+
+    await ableRoom({ id: row.id, enable: !row.enable });
+    Message.success('操作成功!');
+    getList();
+  }
+</script>

+ 179 - 0
src/views/base/room-manage/modifyRoom.vue

@@ -0,0 +1,179 @@
+<template>
+  <a-modal
+    v-model:visible="visible"
+    :width="500"
+    title-align="start"
+    top="10vh"
+    :align-center="false"
+    @before-open="modalBeforeOpen"
+  >
+    <template #title> {{ title }} </template>
+    <a-form ref="formRef" :model="formData" :rules="rules" auto-label-width>
+      <a-form-item field="name" label="考场名称">
+        <a-input
+          v-model.trim="formData.name"
+          placeholder="请输入"
+          allow-clear
+        ></a-input>
+      </a-form-item>
+      <a-form-item field="code" label="考场代码">
+        <a-input
+          v-model.trim="formData.code"
+          placeholder="请输入"
+          allow-clear
+        ></a-input>
+      </a-form-item>
+      <a-form-item field="address" label="考场地址">
+        <a-input
+          v-model.trim="formData.address"
+          placeholder="请输入"
+          allow-clear
+        ></a-input>
+      </a-form-item>
+      <a-form-item field="capacity" label="机房容量">
+        <a-input-number
+          v-model="formData.capacity"
+          placeholder="请输入"
+          :style="{ width: '120px' }"
+          :min="1"
+          :max="99999"
+          :step="1"
+        />
+      </a-form-item>
+      <a-form-item label="教学点">
+        <SelectTeaching
+          v-model="formData.teachingId"
+          placeholder="请选择"
+          allow-clear
+        />
+      </a-form-item>
+      <a-form-item field="agentId" label="考点">
+        <SelectAgent
+          v-model="formData.agentId"
+          placeholder="请选择"
+          allow-clear
+          :teaching-id="formData.teachingId"
+        />
+      </a-form-item>
+    </a-form>
+
+    <template #footer>
+      <a-button type="primary" :disabled="loading" @click="confirm"
+        >确认</a-button
+      >
+      <a-button @click="close">取消</a-button>
+    </template>
+  </a-modal>
+</template>
+
+<script setup lang="ts">
+  import { computed, nextTick, reactive, ref } from 'vue';
+  import { Message } from '@arco-design/web-vue';
+  import type { FormInstance } from '@arco-design/web-vue/es/form';
+  import { updateRoom } from '@/api/base';
+  import useLoading from '@/hooks/loading';
+  import useModal from '@/hooks/modal';
+  import { objAssign, objModifyAssign } from '@/utils/utils';
+  import { RoomItem, RoomUpdateParams } from '@/api/types/base';
+  import { FormRules } from '@/types/global';
+
+  defineOptions({
+    name: 'ModifyRoom',
+  });
+
+  /* modal */
+  const { visible, open, close } = useModal();
+  defineExpose({ open, close });
+
+  const defaultFormData = {
+    id: null,
+    name: '',
+    code: '',
+    address: '',
+    capactiy: undefined,
+    teachingId: '',
+    agentId: '',
+  };
+
+  const props = defineProps<{
+    rowData: RoomItem;
+  }>();
+  const emit = defineEmits(['modified']);
+
+  const isEdit = computed(() => !!props.rowData.id);
+  const title = computed(() => `${isEdit.value ? '编辑' : '新增'}考场`);
+
+  const formRef = ref<FormInstance>();
+  const formData = reactive<RoomUpdateParams>({ ...defaultFormData });
+  const rules: FormRules<RoomUpdateParams> = {
+    name: [
+      {
+        required: true,
+        message: '请输入名称',
+      },
+      {
+        max: 50,
+        message: '名称不能超过50字符',
+      },
+    ],
+    code: [
+      {
+        required: true,
+        message: '请输入代码',
+      },
+      {
+        max: 50,
+        message: '代码不能超过50字符',
+      },
+    ],
+    address: [
+      {
+        required: true,
+        message: '请输入地址',
+      },
+      {
+        max: 100,
+        message: '地址不能超过100字符',
+      },
+    ],
+    agentId: [
+      {
+        required: true,
+        message: '请选择考点',
+      },
+    ],
+    capacity: [
+      {
+        required: true,
+        message: '请输入容量',
+      },
+    ],
+  };
+
+  /* confirm */
+  const { loading, setLoading } = useLoading();
+  async function confirm() {
+    const err = await formRef.value?.validate();
+    if (err) return;
+
+    setLoading(true);
+    const datas = objAssign(formData, {});
+    const res = await updateRoom(datas).catch(() => false);
+    setLoading(false);
+    if (!res) return;
+    Message.success('修改成功!');
+    emit('modified', datas);
+    close();
+  }
+  /* init modal */
+  function modalBeforeOpen() {
+    if (props.rowData.id) {
+      objModifyAssign(formData, props.rowData);
+    } else {
+      objModifyAssign(formData, defaultFormData);
+    }
+    nextTick(() => {
+      formRef.value?.clearValidate();
+    });
+  }
+</script>

+ 1 - 1
src/views/base/teaching-manage/index.vue

@@ -182,7 +182,7 @@
     const action = row.enable ? '禁用' : '启用';
     const confirmRes = await modalConfirm(
       '提示',
-      `确定要${action}任务【${row.name}】吗?`
+      `确定要${action}教学点【${row.name}】吗?`
     ).catch(() => false);
     if (confirmRes !== 'confirm') return;
 

+ 4 - 1
src/views/base/teaching-manage/modifyTeaching.vue

@@ -55,7 +55,7 @@
 </template>
 
 <script setup lang="ts">
-  import { computed, reactive, ref } from 'vue';
+  import { computed, nextTick, reactive, ref } from 'vue';
   import { Message } from '@arco-design/web-vue';
   import type { FormInstance } from '@arco-design/web-vue/es/form';
   import { updateTeaching } from '@/api/base';
@@ -148,5 +148,8 @@
     } else {
       objModifyAssign(formData, defaultFormData);
     }
+    nextTick(() => {
+      formRef.value?.clearValidate();
+    });
   }
 </script>

+ 4 - 1
src/views/order/task-manage/addTimes.vue

@@ -81,7 +81,7 @@
 </template>
 
 <script setup lang="ts">
-  import { computed, reactive, ref } from 'vue';
+  import { computed, nextTick, reactive, ref } from 'vue';
   import type { FormInstance } from '@arco-design/web-vue/es/form';
   import useModal from '@/hooks/modal';
   import { formatDate, objModifyAssign } from '@/utils/utils';
@@ -171,5 +171,8 @@
   /* init modal */
   function modalBeforeOpen() {
     objModifyAssign(formData, getDefaultFormData());
+    nextTick(() => {
+      formRef.value?.clearValidate();
+    });
   }
 </script>

+ 2 - 2
src/views/order/task-manage/noticeForm.vue

@@ -79,7 +79,7 @@
     emit('modified');
   }
   /* init modal */
-  function modalBeforeOpen() {
+  function initData() {
     if (props.rowData.id) {
       objModifyAssign(formData, props.rowData);
     } else {
@@ -87,6 +87,6 @@
     }
   }
   onMounted(() => {
-    modalBeforeOpen();
+    initData();
   });
 </script>

+ 2 - 2
src/views/order/task-manage/ruleForm.vue

@@ -174,7 +174,7 @@
     emit('modified');
   }
   /* init modal */
-  function modalBeforeOpen() {
+  function initData() {
     if (props.rowData.id) {
       objModifyAssign(formData, props.rowData);
     } else {
@@ -182,6 +182,6 @@
     }
   }
   onMounted(() => {
-    modalBeforeOpen();
+    initData();
   });
 </script>

+ 2 - 2
src/views/order/task-manage/timeForm.vue

@@ -103,7 +103,7 @@
     emit('modified');
   }
   /* init */
-  function modalBeforeOpen() {
+  function initData() {
     if (props.rowData.id) {
       objModifyAssign(formData, props.rowData);
     } else {
@@ -111,6 +111,6 @@
     }
   }
   onMounted(() => {
-    modalBeforeOpen();
+    initData();
   });
 </script>