mo 1 年之前
父节点
当前提交
740dabc0c3

+ 14 - 2
src/components/CDatePicker/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, ref } from 'vue';
+import { defineComponent, ref, watch } from 'vue';
 import styles from './index.module.less';
 import { NIcon, NImage, NDatePicker } from 'naive-ui';
 import dateIcons from './images/dateIcon.png';
@@ -13,15 +13,27 @@ export default defineComponent({
     separator: {
       type: String,
       default: '-'
+    },
+    timerValue: {
+      type: Array,
+      default: [] as any
     }
   },
   setup(props, { emit, attrs }) {
     const isFocus = ref(false);
-    const timer = ref(null);
+    const timer = ref(null as any);
     const updateTimer = () => {
       console.log('更新日期', timer.value);
       emit('update:value', timer.value);
     };
+    console.log(props.timerValue, 'timerValue');
+    timer.value = props.timerValue || [];
+    watch(
+      () => props.timerValue,
+      (value: any) => {
+        timer.value = value || [];
+      }
+    );
     return () => (
       <>
         <div class={styles.CdataWrap}>

+ 80 - 0
src/utils/dateFormat.ts

@@ -0,0 +1,80 @@
+import dayjs from 'dayjs';
+
+export function getNowDateAndMonday(time: number) {
+  let timestamp = time;
+  const serverDate = new Date(time);
+  if (serverDate.getDay() == 0) {
+    timestamp -= 7 * 24 * 60 * 60 * 1000;
+  }
+  const mondayTime =
+    timestamp - (serverDate.getDay() - 1) * 24 * 60 * 60 * 1000;
+
+  const mondayData = new Date(mondayTime).getTime();
+  //年
+  // const mondayY = mondayData.getFullYear();
+  // //月
+  // const mondayM =
+  //   mondayData.getMonth() + 1 < 10
+  //     ? '0' + (mondayData.getMonth() + 1)
+  //     : mondayData.getMonth() + 1;
+  // //日
+  // const mondayD =
+  //   mondayData.getDate() < 10
+  //     ? '0' + mondayData.getDate()
+  //     : mondayData.getDate();
+
+  // const str = mondayY + '-' + mondayM + '-' + mondayD;
+  return mondayData;
+}
+export function getNowDateAndSunday(time: number) {
+  const timestamp = time;
+  const serverDate = new Date(time);
+
+  let num = 7 - serverDate.getDay();
+  if (num == 7) {
+    num = 0;
+  }
+  const sundayTiem = timestamp + num * 24 * 60 * 60 * 1000;
+  const SundayData = new Date(sundayTiem).getTime();
+  //年
+  // const tomorrowY = SundayData.getFullYear(); //月
+  // const tomorrowM =
+  //   SundayData.getMonth() + 1 < 10
+  //     ? '0' + (SundayData.getMonth() + 1)
+  //     : SundayData.getMonth() + 1;
+  // //日
+  // const tomorrowD =
+  //   SundayData.getDate() < 10
+  //     ? '0' + SundayData.getDate()
+  //     : SundayData.getDate();
+  // const str = tomorrowY + '-' + tomorrowM + '-' + tomorrowD;
+
+  return SundayData;
+}
+
+export const getTimes = (
+  times: any,
+  keys: Array<string> = [],
+  format = 'YYYY-MM-DD'
+) => {
+  if (times && times.length) {
+    return format == 'YYYY-MM-DD'
+      ? {
+          [keys[0] || 'start']: dayjs(times[0]).isValid()
+            ? dayjs(times[0]).format(format) + ' 00:00:00'
+            : '',
+          [keys[1] || 'end']: dayjs(times[1]).isValid()
+            ? dayjs(times[1]).format(format) + ' 23:59:59'
+            : ''
+        }
+      : {
+          [keys[0] || 'start']: dayjs(times[0]).isValid()
+            ? dayjs(times[0]).format(format)
+            : '',
+          [keys[1] || 'end']: dayjs(times[1]).isValid()
+            ? dayjs(times[1]).format(format)
+            : ''
+        };
+  }
+  return {};
+};

+ 15 - 0
src/utils/request.ts

@@ -25,6 +25,21 @@ request.interceptors.request.use(
     const Authorization = userStore.getToken || '';
     const authHeaders: any = {};
     if (
+      userStore.getUserInfo &&
+      (userStore.getUserInfo.schoolInfos as any) &&
+      userStore.getUserInfo.schoolInfos[0]?.id
+    ) {
+      // console.log(
+      //   userStore.getUserInfo && userStore.getUserInfo.schoolInfos[0]?.id,
+      //   ' userStore.getUserInfo && userStore.getUserInfo.schoolInfos[0]?.id',
+      //   options
+      // );
+
+      options.headers['schoolId'] =
+        (userStore.getUserInfo && userStore.getUserInfo.schoolInfos[0]?.id) ||
+        '';
+    }
+    if (
       Authorization &&
       !['/api-oauth/userlogin', '/api-auth/open/sendSms'].includes(url)
     ) {

+ 67 - 0
src/views/classList/api.ts

@@ -1 +1,68 @@
 import request from '@/utils/request';
+/**
+ * 班级管理 - 班级列表
+ */
+export const classGroupList = (params: any) => {
+  return request.post('/edu-app/classGroup/page', {
+    data: params
+    // requestType: 'form'
+  });
+};
+
+/**
+ * 获取班级里的学生
+ */
+export const getCLassStudent = (params: any) => {
+  return request.post('/edu-app/student/page', {
+    data: params
+    // requestType: 'form'
+  });
+};
+
+/**
+ * 学员调整
+ */
+export const adjustStudent = (params: any) => {
+  return request.post('/edu-app/classGroup/adjustStudent', {
+    data: params
+    // requestType: 'form'
+  });
+};
+
+/**
+ * 删除
+ *
+ */
+export const deleteClass = (params: any) => {
+  return request.post('/edu-app/classGroup/delete', {
+    data: params,
+    requestType: 'form'
+  });
+};
+
+/**
+ * 新建班级
+ */
+export const addClass = (params: any) => {
+  return request.post('/edu-app/classGroup/save', {
+    data: params
+  });
+};
+
+/**
+ * 班级学员
+ */
+export const getStudentList = (params: any) => {
+  return request.post('/edu-app/student/page', {
+    data: params
+  });
+};
+
+/**
+ * 获取训练列表
+ */
+export const getTrainingList = (params: any) => {
+  return request.post('/edu-app/lessonTraining/trainingList', {
+    data: params
+  });
+};

+ 47 - 0
src/views/classList/classDetail.tsx

@@ -0,0 +1,47 @@
+import { defineComponent, ref } from 'vue';
+import styles from './index.module.less';
+import {
+  NTabs,
+  NTabPane,
+  NSpace,
+  NBreadcrumb,
+  NBreadcrumbItem
+} from 'naive-ui';
+import { useRoute, useRouter } from 'vue-router';
+// import CBreadcrumb from '@/src/components/CBreadcrumb';
+import ClassStudent from './components/classStudent';
+import AfterWork from './components/afterWork';
+export default defineComponent({
+  name: 'base-setting',
+  setup(props, { emit, attrs }) {
+    const activeTab = ref('student');
+    const router = useRouter();
+    const route = useRoute();
+    return () => (
+      <div>
+        {/* <CBreadcrumb
+          father="班级管理"
+          toPath="/classList"
+          selfName={(route.query.name as string) || '--'}></CBreadcrumb> */}
+        <div class={styles.listWrap}>
+          <NTabs
+            class={styles.customTabs}
+            v-model:value={activeTab.value}
+            size="large"
+            animated
+            pane-wrapper-style="margin: 0 -4px"
+            pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;">
+            <NTabPane name="student" tab="学员名单">
+              <ClassStudent></ClassStudent>
+            </NTabPane>
+            <NTabPane name="afterWork" tab="课后训练">
+              <AfterWork></AfterWork>
+            </NTabPane>
+            <NTabPane name="practice" tab="练习记录"></NTabPane>
+            <NTabPane name="attendclass" tab="上课记录"></NTabPane>
+          </NTabs>
+        </div>
+      </div>
+    );
+  }
+});

+ 251 - 0
src/views/classList/components/afterWork.tsx

@@ -0,0 +1,251 @@
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import {
+  NButton,
+  NDataTable,
+  NForm,
+  NFormItem,
+  NImage,
+  NModal,
+  NSelect,
+  NSpace
+} from 'naive-ui';
+import SearchInput from '@/components/searchInput';
+import CSelect from '@/components/CSelect';
+import Pagination from '@/components/pagination';
+import { getTrainingList } from '../api';
+import add from './images/add.png';
+import { useRoute } from 'vue-router';
+import CDatePicker from '/src/components/CDatePicker';
+import {
+  getNowDateAndMonday,
+  getNowDateAndSunday,
+  getTimes
+} from '@/utils/dateFormat';
+import dayjs from 'dayjs';
+import TrainSettings from '../../attend-class/model/train-settings';
+export default defineComponent({
+  name: 'afterWork',
+  setup(props, { emit }) {
+    const timer = ref<[number, number]>([
+      getNowDateAndMonday(new Date().getTime()),
+      getNowDateAndSunday(new Date().getTime())
+    ]);
+    const state = reactive({
+      searchForm: {
+        status: null as any
+      },
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 4
+      },
+      tableList: [] as any,
+      addWorkVisible: false
+    });
+    const route = useRoute();
+    const search = () => {
+      state.pagination.page = 1;
+      getList();
+    };
+
+    const onReset = () => {
+      timer.value = [
+        getNowDateAndMonday(new Date().getTime()),
+        getNowDateAndSunday(new Date().getTime())
+      ];
+      state.searchForm = {
+        status: null as any
+      };
+      search();
+    };
+    const getList = async () => {
+      state.loading = true;
+
+      try {
+        const res = await getTrainingList({
+          classGroupId: route.query.id,
+          ...state.searchForm,
+          ...state.pagination,
+          ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
+        });
+
+        state.tableList = res.data.rows;
+
+        state.pagination.pageTotal = res.data.total;
+        state.loading = false;
+      } catch (e) {
+        state.loading = false;
+        console.log(e);
+      }
+    };
+    onMounted(() => {
+      getList();
+    });
+    const columns = () => {
+      return [
+        {
+          title: '布置老师',
+          key: 'teacherName'
+        },
+        {
+          title: '布置时间',
+          key: 'createTime',
+          render(row: any) {
+            return <>{dayjs(row.createTime).format('YYYY-MM-DD')}</>;
+          }
+        },
+        {
+          title: '截止时间',
+          key: 'expireDate',
+          render(row: any) {
+            return <>{dayjs(row.expireDate).format('YYYY-MM-DD')}</>;
+          }
+        },
+        {
+          title: '训练状态',
+          key: 'status',
+          render(row: any) {
+            return row.status == 0 ? (
+              <div class={styles.indDot}>
+                {' '}
+                <span></span> 进行中
+              </div>
+            ) : (
+              <div class={styles.endDot}>
+                <span></span>已结束
+              </div>
+            );
+          }
+        },
+        {
+          title: '布置人数',
+          key: 'expectNum'
+        },
+        {
+          title: '提交人数',
+          key: 'trainingNum'
+        },
+        {
+          title: '合格人数',
+          key: 'standardNum'
+        },
+        {
+          title: '提交率',
+          key: 'trainingRate',
+          render(row: any) {
+            return <>{row.trainingRate}%</>;
+          }
+        },
+        {
+          title: '合格率',
+          key: 'qualifiedRate',
+          render(row: any) {
+            return <>{row.qualifiedRate}%</>;
+          }
+        },
+        // {
+        //   title: '',
+        //   key: 'sex',
+        //   render(row: any) {
+        //     return <>{row.sex == '0' ? '女' : '男'}</>;
+        //   }
+        // },
+        {
+          title: '操作',
+          key: 'id',
+          render(row: any) {
+            return (
+              <NButton text type="primary">
+                详情
+              </NButton>
+            );
+          }
+        }
+      ];
+    };
+    return () => (
+      <div>
+        <div class={styles.searchList}>
+          <NForm label-placement="left" inline>
+            <NFormItem>
+              <CDatePicker
+                v-model:value={timer.value}
+                separator={'至'}
+                type="daterange"
+                timerValue={timer.value}></CDatePicker>
+            </NFormItem>
+
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: [
+                    {
+                      label: '训练状态',
+                      value: null
+                    },
+                    {
+                      label: '已结束',
+                      value: '1'
+                    },
+                    {
+                      label: '进行中',
+                      value: '0'
+                    }
+                  ],
+                  placeholder: '训练状态',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.status}></CSelect>
+            </NFormItem>
+
+            <NFormItem>
+              <NSpace justify="end">
+                <NButton type="primary" class="searchBtn" onClick={search}>
+                  搜索
+                </NButton>
+                <NButton
+                  type="primary"
+                  ghost
+                  class="resetBtn"
+                  onClick={onReset}>
+                  重置
+                </NButton>
+              </NSpace>
+            </NFormItem>
+          </NForm>
+        </div>
+        <NButton
+          class={styles.addBtn}
+          type="primary"
+          onClick={() => (state.addWorkVisible = true)}>
+          布置训练
+        </NButton>
+        <div class={styles.tableWrap}>
+          <NDataTable
+            class={styles.classTable}
+            loading={state.loading}
+            columns={columns()}
+            data={state.tableList}></NDataTable>
+          <Pagination
+            v-model:page={state.pagination.page}
+            v-model:pageSize={state.pagination.rows}
+            v-model:pageTotal={state.pagination.pageTotal}
+            onList={getList}
+            sync
+            saveKey="orchestraRegistration-key"
+          />
+        </div>
+        <NModal
+          v-model:show={state.addWorkVisible}
+          preset="card"
+          class={[styles.attendClassModal, styles.trainClassModal]}
+          title={'训练设置'}>
+          <TrainSettings onClose={() => (state.addWorkVisible = false)} />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 185 - 0
src/views/classList/components/classStudent.tsx

@@ -0,0 +1,185 @@
+import { defineComponent, onMounted, reactive } from 'vue';
+import styles from '../index.module.less';
+import {
+  NButton,
+  NDataTable,
+  NForm,
+  NFormItem,
+  NImage,
+  NSelect,
+  NSpace
+} from 'naive-ui';
+import SearchInput from '@/components/searchInput';
+import CSelect from '@/components/CSelect';
+import Pagination from '@/components/pagination';
+import { getStudentList } from '../api';
+import add from './images/add.png';
+import { useRoute } from 'vue-router';
+export default defineComponent({
+  name: 'student-studentList',
+  setup(props, { emit }) {
+    const state = reactive({
+      searchForm: { keyword: '', gender: null as any },
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 4
+      },
+      tableList: [] as any
+    });
+    const route = useRoute();
+    const search = () => {
+      state.pagination.page = 1;
+      getList();
+      console.log('search', state);
+    };
+
+    const onReset = () => {
+      state.searchForm = { keyword: '', gender: null as any };
+      search();
+    };
+    const getList = async () => {
+      state.loading = true;
+      try {
+        const res = await getStudentList({
+          classGroupId: route.query.id,
+          ...state.searchForm,
+          ...state.pagination
+        });
+
+        state.tableList = res.data.rows;
+
+        state.pagination.pageTotal = res.data.total;
+        state.loading = false;
+      } catch (e) {
+        state.loading = false;
+        console.log(e);
+      }
+    };
+    onMounted(() => {
+      getList();
+    });
+    const columns = () => {
+      return [
+        {
+          title: '学生姓名',
+          key: 'nickname'
+        },
+        {
+          title: '手机号',
+          key: 'phone'
+        },
+        {
+          title: '性别',
+          key: 'sex',
+          render(row: any) {
+            return <>{row.sex == '0' ? '女' : '男'}</>;
+          }
+        },
+
+        // {
+        //   title: '学生类型',
+        //   key: 'studentType',
+        //   render(row: any) {
+        //     return <>{row.studentType == 'member' ? '会员' : '普通'}</>;
+        //   }
+        // },
+        {
+          title: '操作',
+          key: 'id',
+          render(row: any) {
+            return (
+              <NButton text type="primary">
+                详情
+              </NButton>
+            );
+          }
+        }
+      ];
+    };
+    return () => (
+      <div>
+        <div class={styles.searchList}>
+          <NForm label-placement="left" inline>
+            <NFormItem>
+              <SearchInput
+                {...{ placeholder: '请输入学生姓名' }}
+                class={styles.searchInput}
+                searchWord={state.searchForm.keyword}
+                onChangeValue={(val: string) =>
+                  (state.searchForm.keyword = val)
+                }></SearchInput>
+            </NFormItem>
+
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: [
+                    {
+                      label: '选择性别',
+                      value: null
+                    },
+                    {
+                      label: '男',
+                      value: '1'
+                    },
+                    {
+                      label: '女',
+                      value: '0'
+                    }
+                  ],
+                  placeholder: '性别',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.gender}></CSelect>
+            </NFormItem>
+
+            <NFormItem>
+              <NSpace justify="end">
+                <NButton type="primary" class="searchBtn" onClick={search}>
+                  搜索
+                </NButton>
+                <NButton
+                  type="primary"
+                  ghost
+                  class="resetBtn"
+                  onClick={onReset}>
+                  重置
+                </NButton>
+              </NSpace>
+            </NFormItem>
+          </NForm>
+        </div>
+        {/* <NButton
+          class={styles.addBtn}
+          type="primary"
+          v-slots={{
+            icon: () => (
+              <>
+                <NImage class={styles.addBtnIcon} src={add}></NImage>
+              </>
+            )
+          }}>
+          新增学生
+        </NButton> */}
+        <div class={styles.tableWrap}>
+          <NDataTable
+            class={styles.classTable}
+            loading={state.loading}
+            columns={columns()}
+            data={state.tableList}></NDataTable>
+          <Pagination
+            v-model:page={state.pagination.page}
+            v-model:pageSize={state.pagination.rows}
+            v-model:pageTotal={state.pagination.pageTotal}
+            onList={getList}
+            sync
+            saveKey="orchestraRegistration-key"
+          />
+        </div>
+      </div>
+    );
+  }
+});

+ 85 - 0
src/views/classList/contants.ts

@@ -0,0 +1,85 @@
+export const threeYearSystem = [
+  { label: '全部年级', value: null },
+  { label: '七年级', value: 1 },
+  { label: '八年级', value: 2 },
+  { label: '九年级', value: 3 }
+];
+export const foreYearSystem = [
+  { label: '选择年级', value: null },
+  { label: '六年级', value: 1 },
+  { label: '七年级', value: 2 },
+  { label: '八年级', value: 3 },
+  { label: '九年级', value: 4 }
+];
+export const fiveYearSystem = [
+  { label: '选择年级', value: null },
+  { label: '一年级', value: 1 },
+  { label: '二年级', value: 2 },
+  { label: '三年级', value: 3 },
+  { label: '四年级', value: 4 },
+  { label: '五年级', value: 5 }
+];
+export const sixYearSystem = [
+  { label: '全部年级', value: null },
+  { label: '一年级', value: 1 },
+  { label: '二年级', value: 2 },
+  { label: '三年级', value: 3 },
+  { label: '四年级', value: 4 },
+  { label: '五年级', value: 5 },
+  { label: '六年级', value: 6 }
+];
+export const nineYearSystem = [
+  { label: '选择年级', value: null },
+  { label: '一年级', value: 1 },
+  { label: '二年级', value: 2 },
+  { label: '三年级', value: 3 },
+  { label: '四年级', value: 4 },
+  { label: '五年级', value: 5 },
+  { label: '六年级', value: 6 },
+  { label: '七年级', value: 7 },
+  { label: '八年级', value: 8 },
+  { label: '九年级', value: 9 }
+];
+export const classArray = [
+  { value: null, label: '选择班级' },
+  { value: 1, label: '1班' },
+  { value: 2, label: '2班' },
+  { value: 3, label: '3班' },
+  { value: 4, label: '4班' },
+  { value: 5, label: '5班' },
+  { value: 6, label: '6班' },
+  { value: 7, label: '7班' },
+  { value: 8, label: '8班' },
+  { value: 9, label: '9班' },
+  { value: 10, label: '10班' },
+  { value: 11, label: '11班' },
+  { value: 12, label: '12班' },
+  { value: 13, label: '13班' },
+  { value: 14, label: '14班' },
+  { value: 15, label: '15班' },
+  { value: 16, label: '16班' },
+  { value: 17, label: '17班' },
+  { value: 18, label: '18班' },
+  { value: 19, label: '19班' },
+  { value: 20, label: '20班' },
+  { value: 21, label: '21班' },
+  { value: 22, label: '22班' },
+  { value: 23, label: '23班' },
+  { value: 24, label: '24班' },
+  { value: 25, label: '25班' },
+  { value: 26, label: '26班' },
+  { value: 27, label: '27班' },
+  { value: 28, label: '28班' },
+  { value: 29, label: '29班' },
+  { value: 30, label: '30班' },
+  { value: 31, label: '31班' },
+  { value: 32, label: '32班' },
+  { value: 33, label: '33班' },
+  { value: 34, label: '34班' },
+  { value: 35, label: '35班' },
+  { value: 36, label: '36班' },
+  { value: 37, label: '37班' },
+  { value: 38, label: '38班' },
+  { value: 39, label: '39班' },
+  { value: 40, label: '40班' }
+];

+ 340 - 0
src/views/classList/index.module.less

@@ -22,3 +22,343 @@
     }
   }
 }
+.btnGroup {
+  padding: 40px 0;
+
+  :global {
+    .n-button {
+      height: 47px;
+      min-width: 156px;
+    }
+  }
+}
+.resetStudentWrap {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding-top: 40px;
+  .studentTransfer {
+    position: relative;
+    .smallArrow {
+      right: 166px;
+      top: 15px;
+      position: absolute;
+      width: 12px;
+      height: 12px;
+      cursor: pointer;
+      z-index: 1000;
+    }
+
+    :global {
+      .n-legacy-transfer {
+        width: 634px;
+        min-height: 340px;
+        .n-legacy-transfer-list {
+          &:nth-child(1) {
+            .n-legacy-transfer-list-header__header {
+              &::after {
+                content: '(当前班级)';
+                font-size: 12px;
+                color: #777;
+                font-weight: 400;
+              }
+            }
+          }
+        }
+      }
+      .n-legacy-transfer-list-item {
+        &:hover {
+          background-color: #fff !important;
+        }
+      }
+      .n-legacy-transfer-list__border {
+        border: none;
+      }
+      .n-legacy-transfer-list-header__extra {
+        display: none;
+      }
+      .n-legacy-transfer-list-header {
+        display: flex;
+        flex-direction: column;
+        background: #e8f2ff;
+        align-items: flex-start;
+        height: auto;
+      }
+      .n-legacy-transfer-list-header__checkbox {
+        order: 2;
+        padding: 0 !important;
+        &::after {
+          content: '全选';
+          margin-left: 5px;
+        }
+        margin-top: 8px;
+      }
+      .n-legacy-transfer-list-header__header {
+        width: 100%;
+        padding: 14px 0;
+        font-size: 16px;
+        font-weight: 600 !important;
+        color: #131415;
+        line-height: 22px;
+        order: 1;
+        border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+      }
+      .n-legacy-transfer-list {
+        padding: 0 16px;
+        min-height: 340px;
+        background: #e8f2ff;
+        border-radius: 16px 16px 0 0;
+        width: 277px;
+        .n-legacy-transfer-filter {
+          background: #e8f2ff;
+          padding: 8px 0px 0px;
+          border-bottom: none;
+        }
+        .n-input {
+          border-radius: 8px;
+          .n-input-wrapper {
+            .n-input__border {
+              border-radius: 8px;
+            }
+            .n-input__input-el {
+              height: 41px;
+              line-height: 41px;
+              background: #ffffff;
+            }
+          }
+        }
+      }
+      .n-legacy-transfer-gap {
+        width: 80px;
+        .n-button {
+          width: 34px;
+          height: 34px;
+          border-radius: 8px;
+          .n-button__state-border {
+            border: none !important;
+          }
+          &:nth-child(1) {
+            transform: rotate(180deg);
+            position: relative;
+            &:hover {
+              &::after {
+                background: url('@{img}/transArrowActive.png') no-repeat;
+                top: 0;
+                left: 0;
+
+                background-size: 34px 34px;
+              }
+            }
+            &::after {
+              position: absolute;
+              content: '';
+              width: 34px;
+              height: 34px;
+              background-color: black;
+              top: 0;
+              left: 0;
+              background: url('@{img}/transArrrow.png') no-repeat;
+              background-size: 34px 34px;
+              z-index: 100;
+            }
+          }
+          &:nth-child(2) {
+            position: relative;
+            &:hover {
+              &::after {
+                background: url('@{img}/transArrowActive.png') no-repeat;
+                top: 0;
+                left: 0;
+
+                background-size: 34px 34px;
+              }
+            }
+            &::after {
+              position: absolute;
+              content: '';
+              width: 100%;
+              height: 100%;
+              background-color: black;
+              top: 0;
+              left: 0;
+              background: url('@{img}/transArrrow.png') no-repeat;
+              background-size: 34px 34px;
+              z-index: 100;
+            }
+          }
+        }
+      }
+    }
+    .studentTransferBottom {
+      width: 100%;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: space-between;
+
+      .bottom {
+        padding: 0 16px;
+
+        width: 277px;
+        background-color: #e8f2ff;
+
+        border-radius: 0 0 8px 8px;
+        .bottomWrap {
+          padding: 14px 0;
+          border-top: 1px solid rgba(0, 0, 0, 0.06);
+        }
+      }
+    }
+  }
+}
+.studentTransferList {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  .studentLeft,
+  .studentRight {
+    padding: 14px 16px;
+    width: 277px;
+
+    background: #e8f2ff;
+    border-radius: 16px;
+    .listTop {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      padding-bottom: 14px;
+      border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+      font-size: 16px;
+      font-weight: 600;
+      color: #131415;
+      line-height: 22px;
+      span {
+        color: #777;
+        font-weight: 400;
+        font-size: 12px;
+      }
+    }
+    .chioseCheckBox {
+      padding: 2px 2px;
+      font-size: 16px;
+      font-weight: 400;
+      color: #131415;
+    }
+  }
+
+  .chioseBox {
+    width: 80px;
+    height: 389px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    .chioseBtn {
+      cursor: pointer;
+      width: 34px;
+      height: 34px;
+      border-radius: 8px;
+      background: url('@{img}/transArrrow.png') no-repeat;
+      background-size: 34px 34px;
+      &:hover {
+        background: url('@{img}/transArrowActive.png') no-repeat;
+        background-size: 34px 34px;
+      }
+    }
+    .chioseBtnRight {
+      transform: rotate(180deg);
+      margin-bottom: 23px;
+    }
+  }
+  .chioseCheckAllBox {
+    margin-top: 8px !important;
+    margin-bottom: 14px !important;
+  }
+  .bottom {
+    padding-top: 14px;
+    border-top: 1px solid rgba(0, 0, 0, 0.06);
+  }
+}
+.addClass {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding-top: 50px;
+}
+
+.listWrap {
+  min-height: 805px;
+  padding: 32px;
+  background-color: #fff;
+  border-radius: 20px;
+
+  .customTabs {
+    :global {
+      .n-tabs-tab--active {
+        font-size: 18px !important;
+
+        font-weight: 600 !important;
+        color: #131415 !important;
+      }
+
+      .n-tabs-tab {
+        font-size: 18px;
+        padding: 8px 0 !important;
+        font-weight: 400;
+        min-width: 50px;
+        color: #8b8d98;
+
+        &:hover {
+          color: #198cfe !important;
+        }
+      }
+
+      .n-tabs-bar {
+        // background-color: red !important;
+        width: 50px !important;
+        height: 5px !important;
+        background: url('@{img}/barIcon.png') no-repeat;
+        background-size: 50px 5px;
+      }
+    }
+  }
+}
+.indDot,
+.endDot {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.indDot {
+  span {
+    border-radius: 50%;
+    width: 8px;
+    height: 8px;
+    background: #198cfe;
+    margin-right: 8px;
+  }
+}
+
+.endDot {
+  opacity: 0.7;
+  color: #aaaaaa;
+
+  span {
+    border-radius: 50%;
+    width: 8px;
+    height: 8px;
+    background: #aaaaaa;
+    margin-right: 8px;
+  }
+}
+
+.attendClassModal {
+  width: 1100px;
+
+  :global {
+    .n-select-menu {
+      --n-height: calc(var(--n-option-height) * 5.6) !important;
+    }
+  }
+}