|
@@ -1,4 +1,4 @@
|
|
|
-import { defineComponent, reactive } from 'vue';
|
|
|
+import { computed, defineComponent, onMounted, reactive } from 'vue';
|
|
|
import styles from './index.module.less';
|
|
|
import {
|
|
|
NAvatar,
|
|
@@ -8,21 +8,150 @@ import {
|
|
|
NInput,
|
|
|
NScrollbar,
|
|
|
NSelect,
|
|
|
- NSpace
|
|
|
+ NSpace,
|
|
|
+ NSpin
|
|
|
} from 'naive-ui';
|
|
|
+import defultHeade from '@/components/layout/images/teacherIcon.png';
|
|
|
import SearchInput from '/src/components/searchInput';
|
|
|
+import { useCatchStore } from '/src/store/modules/catchData';
|
|
|
+import { getStudentList } from '/src/views/classList/api';
|
|
|
+import { useThrottleFn } from '@vueuse/core';
|
|
|
+import TheEmpty from '/src/components/TheEmpty';
|
|
|
|
|
|
export default defineComponent({
|
|
|
name: 'assign-student',
|
|
|
- setup() {
|
|
|
+ props: {
|
|
|
+ /** 班级列表 */
|
|
|
+ classList: {
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ selectIds: {
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ classGroupId: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ emits: ['close', 'confirm'],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const catchStore = useCatchStore();
|
|
|
const state = reactive({
|
|
|
studentName: '',
|
|
|
+ loading: false,
|
|
|
+ finshed: false, // 是否加载完
|
|
|
+ checkAllStatus: false,
|
|
|
+ indeterminate: false,
|
|
|
searchFrom: {
|
|
|
- classGroupId: '',
|
|
|
+ currentGradeNum: '',
|
|
|
+ classGroupId: props.classGroupId || '',
|
|
|
subjectId: '',
|
|
|
keyword: ''
|
|
|
},
|
|
|
- tableList: [] as any
|
|
|
+ pagination: {
|
|
|
+ page: 1,
|
|
|
+ rows: 10,
|
|
|
+ pageTotal: 0
|
|
|
+ },
|
|
|
+ tableList: [] as any,
|
|
|
+ checkboxIds: [] as any,
|
|
|
+ selectStudents: [] as any,
|
|
|
+ selectKeyword: ''
|
|
|
+ });
|
|
|
+
|
|
|
+ const getStudentLists = async () => {
|
|
|
+ try {
|
|
|
+ if (state.pagination.page === 1) {
|
|
|
+ state.loading = true;
|
|
|
+ state.tableList = [];
|
|
|
+ }
|
|
|
+ const { data } = await getStudentList({
|
|
|
+ ...state.searchFrom,
|
|
|
+ ...state.pagination
|
|
|
+ });
|
|
|
+ state.loading = false;
|
|
|
+ const rows = data.rows || [];
|
|
|
+ state.tableList.push(...rows);
|
|
|
+ state.finshed = data.pages <= data.current ? true : false;
|
|
|
+ } catch {
|
|
|
+ //
|
|
|
+ state.loading = false;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const onSearch = () => {
|
|
|
+ state.pagination.page = 1;
|
|
|
+ getStudentLists();
|
|
|
+ };
|
|
|
+
|
|
|
+ const selectStudentEmpty = computed(() => {
|
|
|
+ let status = true;
|
|
|
+ state.selectStudents.forEach((item: any) => {
|
|
|
+ if (!item.hide) {
|
|
|
+ status = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return status;
|
|
|
+ });
|
|
|
+
|
|
|
+ const throttledFn = useThrottleFn(() => {
|
|
|
+ state.pagination.page = state.pagination.page + 1;
|
|
|
+ getStudentLists();
|
|
|
+ }, 500);
|
|
|
+
|
|
|
+ // 切换学生状态
|
|
|
+ const onCheckStudents = () => {
|
|
|
+ state.selectStudents = [];
|
|
|
+ if (state.checkboxIds.length <= 0) {
|
|
|
+ state.indeterminate = false;
|
|
|
+ state.checkAllStatus = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (state.checkboxIds.length === state.tableList.length) {
|
|
|
+ state.checkAllStatus = true;
|
|
|
+ state.indeterminate = false;
|
|
|
+ } else {
|
|
|
+ state.checkAllStatus = false;
|
|
|
+ state.indeterminate = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 右边数据
|
|
|
+ state.tableList.forEach((item: any) => {
|
|
|
+ if (state.checkboxIds.includes(item.id)) {
|
|
|
+ state.selectStudents.push(item);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 删除用户
|
|
|
+ const onRemove = (item: any) => {
|
|
|
+ const index = state.checkboxIds.findIndex((id: any) => id === item.id);
|
|
|
+ if (index !== -1) {
|
|
|
+ state.checkboxIds.splice(index, 1);
|
|
|
+ onCheckStudents();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const onSave = () => {
|
|
|
+ const studentInfo: any[] = [];
|
|
|
+ state.selectStudents.forEach((item: any) => {
|
|
|
+ studentInfo.push({
|
|
|
+ id: item.id,
|
|
|
+ name: item.nickname
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ emit('confirm', studentInfo);
|
|
|
+ };
|
|
|
+
|
|
|
+ onMounted(async () => {
|
|
|
+ state.checkboxIds = props.selectIds || [];
|
|
|
+ state.loading = true;
|
|
|
+ await catchStore.getSubjects();
|
|
|
+ await getStudentLists();
|
|
|
+ onCheckStudents();
|
|
|
});
|
|
|
return () => (
|
|
|
<div class={styles.assignStudent}>
|
|
@@ -31,77 +160,159 @@ export default defineComponent({
|
|
|
<div class={styles.searchSpace}>
|
|
|
<NSelect
|
|
|
placeholder="全部班级"
|
|
|
+ disabled={props.classGroupId ? true : false}
|
|
|
v-model:value={state.searchFrom.classGroupId}
|
|
|
+ onUpdate:value={() => onSearch()}
|
|
|
+ options={
|
|
|
+ [{ label: '全部班级', value: '' }, ...props.classList] as any
|
|
|
+ }
|
|
|
/>
|
|
|
<NSelect
|
|
|
+ options={[
|
|
|
+ { label: '全部声部', value: '' },
|
|
|
+ ...catchStore.getSubjectList
|
|
|
+ ]}
|
|
|
placeholder="全部声部"
|
|
|
v-model:value={state.searchFrom.subjectId}
|
|
|
+ onUpdate:value={() => onSearch()}
|
|
|
/>
|
|
|
</div>
|
|
|
<SearchInput
|
|
|
{...{ placeholder: '请输入学生姓名/手机号' }}
|
|
|
class={styles.searchInput}
|
|
|
searchWord={state.searchFrom.keyword}
|
|
|
- onChangeValue={(val: string) =>
|
|
|
- (state.searchFrom.keyword = val)
|
|
|
- }></SearchInput>
|
|
|
+ onChangeValue={(val: string) => {
|
|
|
+ state.searchFrom.keyword = val;
|
|
|
+ }}
|
|
|
+ onClear={() => {
|
|
|
+ state.searchFrom.keyword = '';
|
|
|
+ onSearch();
|
|
|
+ }}
|
|
|
+ onKeyup={(e: KeyboardEvent) => {
|
|
|
+ if (e.code === 'Enter') {
|
|
|
+ onSearch();
|
|
|
+ }
|
|
|
+ }}></SearchInput>
|
|
|
</div>
|
|
|
|
|
|
<div class={styles.studentSection}>
|
|
|
<div class={styles.checkboxAll}>
|
|
|
- <NCheckbox></NCheckbox>
|
|
|
+ <NCheckbox
|
|
|
+ v-model:checked={state.checkAllStatus}
|
|
|
+ indeterminate={state.indeterminate}
|
|
|
+ onUpdate:checked={(val: any) => {
|
|
|
+ if (val) {
|
|
|
+ const ids: any = [];
|
|
|
+ state.tableList.forEach((item: any) => {
|
|
|
+ ids.push(item.id);
|
|
|
+ });
|
|
|
+ state.checkboxIds = ids;
|
|
|
+ } else {
|
|
|
+ state.checkboxIds = [];
|
|
|
+ state.indeterminate = false;
|
|
|
+ }
|
|
|
+ onCheckStudents();
|
|
|
+ }}></NCheckbox>
|
|
|
<p>
|
|
|
- 全选 <span class={styles.nums}>(120)</span> :
|
|
|
+ 全选 <span class={styles.nums}>({state.tableList.length})</span>{' '}
|
|
|
+ :
|
|
|
</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <NScrollbar class={styles.student}>
|
|
|
- <NCheckboxGroup>
|
|
|
- <div class={[styles.studentItem, styles.active]}>
|
|
|
- <NCheckbox></NCheckbox>
|
|
|
- <div class={styles.studentInfo}>
|
|
|
- <NAvatar class={styles.studentImg} />
|
|
|
- <div class={styles.studentValue}>
|
|
|
- <div class={styles.userInfo}>
|
|
|
- <span class={styles.name}>林夏喜</span>
|
|
|
- <i class={styles.iconMember}></i>
|
|
|
- <span class={styles.className}>三年级1班</span>
|
|
|
+ <NScrollbar
|
|
|
+ class={styles.student}
|
|
|
+ onScroll={(e: any) => {
|
|
|
+ const clientHeight = e.target?.clientHeight;
|
|
|
+ const scrollTop = e.target?.scrollTop;
|
|
|
+ const scrollHeight = e.target?.scrollHeight;
|
|
|
+ // 是否到底,是否加载完
|
|
|
+ if (
|
|
|
+ clientHeight + scrollTop + 20 >= scrollHeight &&
|
|
|
+ !state.finshed &&
|
|
|
+ !state.loading
|
|
|
+ ) {
|
|
|
+ throttledFn();
|
|
|
+ }
|
|
|
+ }}>
|
|
|
+ <NSpin show={state.loading} class={styles.loadingSection}>
|
|
|
+ <NCheckboxGroup
|
|
|
+ v-model:value={state.checkboxIds}
|
|
|
+ onUpdate:value={onCheckStudents}>
|
|
|
+ {state.tableList.map((item: any) => (
|
|
|
+ <div class={[styles.studentItem]}>
|
|
|
+ <NCheckbox value={item.id}></NCheckbox>
|
|
|
+ <div class={styles.studentInfo}>
|
|
|
+ <NAvatar
|
|
|
+ src={item.avatar || defultHeade}
|
|
|
+ class={styles.studentImg}
|
|
|
+ />
|
|
|
+ <div class={styles.studentValue}>
|
|
|
+ <div class={styles.userInfo}>
|
|
|
+ <span class={styles.name}>{item.nickname}</span>
|
|
|
+ {item.membership && <i class={styles.iconMember}></i>}
|
|
|
+ {item.classGroupName && (
|
|
|
+ <span class={styles.className}>
|
|
|
+ {item.classGroupName}
|
|
|
+ </span>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div class={styles.phone}>{item.phone}</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class={styles.phone}>15527262536</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </NCheckboxGroup>
|
|
|
+ ))}
|
|
|
+ </NCheckboxGroup>
|
|
|
+ {state.tableList.length <= 0 && !state.loading && <TheEmpty />}
|
|
|
+ </NSpin>
|
|
|
</NScrollbar>
|
|
|
</div>
|
|
|
|
|
|
<div class={styles.selectStudentGroup}>
|
|
|
<div class={styles.selectCount}>
|
|
|
- 当前选中 <span>(40) </span>:
|
|
|
+ 当前选中 <span>({state.selectStudents.length}) </span>:
|
|
|
</div>
|
|
|
<div class={styles.searchSection}>
|
|
|
<SearchInput
|
|
|
{...{ placeholder: '请输入学生姓名' }}
|
|
|
class={styles.searchInput}
|
|
|
- // searchWord={state.searchForm.keyword}
|
|
|
- // onChangeValue={(val: string) =>
|
|
|
- // (state.searchForm.keyword = val)
|
|
|
- // }
|
|
|
- ></SearchInput>
|
|
|
+ searchWord={state.selectKeyword}
|
|
|
+ onChangeValue={(val: string) => {
|
|
|
+ state.selectKeyword = val;
|
|
|
+
|
|
|
+ state.selectStudents.forEach((item: any) => {
|
|
|
+ if (item.nickname?.indexOf(val) === -1) {
|
|
|
+ item.hide = true;
|
|
|
+ } else {
|
|
|
+ item.hide = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }}></SearchInput>
|
|
|
</div>
|
|
|
<NScrollbar class={styles.student}>
|
|
|
- <div class={[styles.studentItem, styles.active]}>
|
|
|
- <div class={styles.studentInfo}>
|
|
|
- <NAvatar class={styles.studentImg} />
|
|
|
- <span class={styles.name}>林夏喜</span>
|
|
|
+ {state.selectStudents.map((student: any) => (
|
|
|
+ <div class={[styles.studentItem, student.hide && styles.hide]}>
|
|
|
+ <div class={styles.studentInfo}>
|
|
|
+ <NAvatar
|
|
|
+ src={student.avatar || defultHeade}
|
|
|
+ class={styles.studentImg}
|
|
|
+ />
|
|
|
+ <span class={styles.name}>{student.nickname}</span>
|
|
|
+ </div>
|
|
|
+ <i
|
|
|
+ class={styles.iconClose}
|
|
|
+ onClick={() => onRemove(student)}></i>
|
|
|
</div>
|
|
|
- <i class={styles.iconClose}></i>
|
|
|
- </div>
|
|
|
+ ))}
|
|
|
+ {selectStudentEmpty.value && <TheEmpty />}
|
|
|
</NScrollbar>
|
|
|
|
|
|
<NSpace justify="end" class={styles.btnGroup}>
|
|
|
- <NButton type="default">取消</NButton>
|
|
|
- <NButton type="primary">保存</NButton>
|
|
|
+ <NButton type="default" onClick={() => emit('close')}>
|
|
|
+ 取消
|
|
|
+ </NButton>
|
|
|
+ <NButton type="primary" onClick={onSave}>
|
|
|
+ 保存
|
|
|
+ </NButton>
|
|
|
</NSpace>
|
|
|
</div>
|
|
|
</div>
|