index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. import { computed, defineComponent, onMounted, reactive } from 'vue';
  2. import styles from './index.module.less';
  3. import {
  4. NAvatar,
  5. NButton,
  6. NCascader,
  7. NCheckbox,
  8. NCheckboxGroup,
  9. NInput,
  10. NScrollbar,
  11. NSelect,
  12. NSpace,
  13. NSpin
  14. } from 'naive-ui';
  15. import defultHeade from '@/components/layout/images/teacherIcon.png';
  16. import SearchInput from '/src/components/searchInput';
  17. import { useCatchStore } from '/src/store/modules/catchData';
  18. import { getStudentList } from '/src/views/classList/api';
  19. import { useThrottleFn } from '@vueuse/core';
  20. import TheEmpty from '/src/components/TheEmpty';
  21. import { getGradeYearList } from '/src/views/home/api';
  22. import { api_getCurrentGradeYear } from '/src/views/studentList/api';
  23. export default defineComponent({
  24. name: 'assign-student',
  25. props: {
  26. /** 班级列表 */
  27. classList: {
  28. type: Array,
  29. default: () => []
  30. },
  31. /** 所选学生列表 */
  32. studentList: {
  33. type: Array,
  34. default: () => []
  35. },
  36. /** 学年 */
  37. currentGradeNum: {
  38. type: [String || Number],
  39. default: ''
  40. },
  41. selectIds: {
  42. type: Array,
  43. default: () => []
  44. },
  45. classGroupId: {
  46. type: String,
  47. default: ''
  48. }
  49. },
  50. emits: ['close', 'confirm'],
  51. setup(props, { emit }) {
  52. const catchStore = useCatchStore();
  53. const state = reactive({
  54. studentName: '',
  55. loading: false,
  56. finshed: false, // 是否加载完
  57. checkAllStatus: false,
  58. indeterminate: false,
  59. searchFrom: {
  60. upgradeFlag: true,
  61. currentGradeNums: null as any,
  62. gradeYear: null,
  63. classGroupId: props.classGroupId || '',
  64. classInstrumentId: '',
  65. keyword: ''
  66. },
  67. pagination: {
  68. page: 1,
  69. rows: 20,
  70. pageTotal: 0
  71. },
  72. tableList: [] as any,
  73. checkboxIds: [] as any,
  74. selectStudents: [] as any,
  75. selectKeyword: '',
  76. popSelectYearList: [] as any
  77. });
  78. // 获取学年
  79. const getYearList = async () => {
  80. try {
  81. const { data } = await api_getCurrentGradeYear({});
  82. state.searchFrom.gradeYear = data;
  83. } catch {
  84. //
  85. }
  86. };
  87. const getStudentLists = async () => {
  88. try {
  89. if (state.pagination.page === 1) {
  90. state.loading = true;
  91. state.tableList = [];
  92. }
  93. const { data } = await getStudentList({
  94. ...state.searchFrom,
  95. ...state.pagination
  96. });
  97. state.loading = false;
  98. const rows = data.rows || [];
  99. state.tableList.push(...rows);
  100. state.finshed = data.pages <= data.current ? true : false;
  101. onCheckStudents();
  102. } catch {
  103. //
  104. state.loading = false;
  105. }
  106. };
  107. const onSearch = () => {
  108. state.pagination.page = 1;
  109. getStudentLists();
  110. };
  111. const selectStudentEmpty = computed(() => {
  112. let status = true;
  113. state.selectStudents.forEach((item: any) => {
  114. if (!item.hide) {
  115. status = false;
  116. }
  117. });
  118. return status;
  119. });
  120. const throttledFn = useThrottleFn(() => {
  121. state.pagination.page = state.pagination.page + 1;
  122. getStudentLists();
  123. }, 500);
  124. // 切换学生状态
  125. const onCheckStudents = () => {
  126. if (state.tableList.length <= 0) {
  127. state.indeterminate = false;
  128. state.checkAllStatus = false;
  129. return;
  130. }
  131. // 右边数据
  132. state.tableList.forEach((item: any) => {
  133. if (state.checkboxIds.includes(item.id)) {
  134. const index = state.selectStudents.findIndex(
  135. (select: any) => select.id == item.id
  136. );
  137. if (index === -1) state.selectStudents.push(item);
  138. } else {
  139. const index = state.selectStudents.findIndex(
  140. (select: any) => select.id == item.id
  141. );
  142. if(index >= 0) state.selectStudents.splice(index, 1)
  143. }
  144. });
  145. let count = 0;
  146. state.tableList.forEach((item: any) => {
  147. const index = state.selectStudents.findIndex(
  148. (select: any) => select.id === item.id
  149. );
  150. if(index >= 0) count++
  151. })
  152. if (count >= state.tableList.length) {
  153. state.checkAllStatus = true;
  154. state.indeterminate = false;
  155. } else {
  156. state.checkAllStatus = false;
  157. state.indeterminate = count === 0 ? false : true;
  158. }
  159. };
  160. // 删除用户
  161. const onRemove = (item: any) => {
  162. const index = state.checkboxIds.findIndex((id: any) => id == item.id);
  163. if (index !== -1) {
  164. state.checkboxIds.splice(index, 1);
  165. const sIndex = state.selectStudents.findIndex(
  166. (select: any) => select.id === item.id
  167. );
  168. if (sIndex !== -1) {
  169. state.selectStudents.splice(sIndex, 1);
  170. }
  171. onCheckStudents();
  172. }
  173. };
  174. const onSave = () => {
  175. const studentInfo: any[] = [];
  176. state.selectStudents.forEach((item: any) => {
  177. studentInfo.push({
  178. id: item.id,
  179. name: item.nickname,
  180. avatar: item.avatar
  181. });
  182. });
  183. emit('confirm', studentInfo);
  184. };
  185. onMounted(async () => {
  186. console.log(props.currentGradeNum, 'props.currentGradeNum-----')
  187. if(Array.isArray(props.currentGradeNum)) {
  188. state.searchFrom.currentGradeNums = props.currentGradeNum.join(',')
  189. } else {
  190. state.searchFrom.currentGradeNums = props.currentGradeNum
  191. }
  192. state.checkboxIds = props.selectIds || [];
  193. state.loading = true;
  194. await catchStore.getSubjects();
  195. await getYearList();
  196. await getStudentLists();
  197. // onCheckStudents();
  198. // 重置选择的学生
  199. state.selectStudents = props.studentList?.map((item: any) => {
  200. return {
  201. ...item,
  202. nickname: item.name
  203. };
  204. });
  205. });
  206. return () => (
  207. <div class={[styles.assignStudent, 'assignStudent']}>
  208. <div class={styles.studentListGroup}>
  209. <div class={styles.searchSection}>
  210. <div class={styles.searchSpace}>
  211. <NSelect
  212. placeholder="全部班级"
  213. disabled={props.classGroupId ? true : false}
  214. labelField="defaultLabel"
  215. filterable
  216. clearable
  217. v-model:value={state.searchFrom.classGroupId}
  218. onUpdate:value={() => onSearch()}
  219. options={
  220. [{ defaultLabel: '全部班级', value: '' }, ...props.classList] as any
  221. }
  222. />
  223. <NCascader
  224. options={[
  225. { name: '全部乐器', id: '' },
  226. ...catchStore.getSubjectList
  227. ]}
  228. placeholder="全部乐器"
  229. v-model:value={state.searchFrom.classInstrumentId}
  230. onUpdate:value={() => onSearch()}
  231. checkStrategy="child"
  232. showPath={false}
  233. childrenField="instruments"
  234. expandTrigger="hover"
  235. labelField="name"
  236. valueField="id"
  237. clearable
  238. filterable
  239. />
  240. </div>
  241. <SearchInput
  242. {...{ placeholder: '请输入学生姓名/手机号' }}
  243. class={styles.searchInput}
  244. searchWord={state.searchFrom.keyword}
  245. onChangeValue={(val: string) => {
  246. state.searchFrom.keyword = val;
  247. }}
  248. onClear={() => {
  249. state.searchFrom.keyword = '';
  250. onSearch();
  251. }}
  252. onKeyup={(e: KeyboardEvent) => {
  253. if (e.code === 'Enter') {
  254. onSearch();
  255. }
  256. }}></SearchInput>
  257. </div>
  258. <div class={styles.studentSection}>
  259. <div class={styles.checkboxAll}>
  260. <NCheckbox
  261. v-model:checked={state.checkAllStatus}
  262. indeterminate={state.indeterminate}
  263. onUpdate:checked={(val: any) => {
  264. if (val) {
  265. const ids: any = [];
  266. state.tableList.forEach((item: any) => {
  267. ids.push(item.id);
  268. });
  269. ids.forEach((id: any) => {
  270. if(!state.checkboxIds.includes(id)) {
  271. state.checkboxIds.push(id)
  272. }
  273. })
  274. } else {
  275. const removeIds = state.tableList.map((item: any) => item.id)
  276. console.log(removeIds, 'removeIds', state.checkboxIds)
  277. removeIds.forEach((rid: any) => {
  278. const index = state.checkboxIds.findIndex((id: any) => id == rid);
  279. if(index !== -1) state.checkboxIds.splice(index, 1);
  280. const sindex = state.selectStudents.findIndex(
  281. (select: any) => select.id == rid
  282. );
  283. if(sindex !== -1) state.selectStudents.splice(sindex, 1)
  284. })
  285. state.indeterminate = false;
  286. }
  287. onCheckStudents();
  288. }}></NCheckbox>
  289. <p>
  290. 全选 <span class={styles.nums}>({state.tableList.length})</span>{' '}
  291. </p>
  292. </div>
  293. </div>
  294. <NScrollbar
  295. class={styles.student}
  296. onScroll={(e: any) => {
  297. const clientHeight = e.target?.clientHeight;
  298. const scrollTop = e.target?.scrollTop;
  299. const scrollHeight = e.target?.scrollHeight;
  300. // 是否到底,是否加载完
  301. if (
  302. clientHeight + scrollTop + 20 >= scrollHeight &&
  303. !state.finshed &&
  304. !state.loading
  305. ) {
  306. throttledFn();
  307. }
  308. }}>
  309. <NSpin show={state.loading} class={styles.loadingSection}>
  310. <NCheckboxGroup
  311. v-model:value={state.checkboxIds}
  312. onUpdate:value={() => {
  313. // state.selectStudents = [];
  314. onCheckStudents();
  315. }}>
  316. {state.tableList.map((item: any) => (
  317. <NCheckbox value={item.id} class={[styles.studentItem]}>
  318. <div class={styles.studentInfo}>
  319. <NAvatar
  320. src={item.avatar || defultHeade}
  321. class={styles.studentImg}
  322. />
  323. <div class={styles.studentValue}>
  324. <div class={styles.userInfo}>
  325. <span class={styles.name}>{item.nickname}</span>
  326. {item.membership && <i class={styles.iconMember}></i>}
  327. {item.classGroupName && (
  328. <span class={styles.className}>
  329. {item.classGroupName}
  330. </span>
  331. )}
  332. </div>
  333. <div class={styles.phone}>{item.phone}</div>
  334. </div>
  335. </div>
  336. </NCheckbox>
  337. ))}
  338. </NCheckboxGroup>
  339. {state.tableList.length <= 0 && !state.loading && <TheEmpty />}
  340. </NSpin>
  341. </NScrollbar>
  342. </div>
  343. <div class={styles.selectStudentGroup}>
  344. <div class={styles.selectCount}>
  345. 当前选中 <span>({state.selectStudents.length}) </span>:
  346. </div>
  347. <div class={styles.searchSection}>
  348. <SearchInput
  349. {...{ placeholder: '请输入学生姓名' }}
  350. class={styles.searchInput}
  351. searchWord={state.selectKeyword}
  352. onChangeValue={(val: string) => {
  353. state.selectKeyword = val;
  354. state.selectStudents.forEach((item: any) => {
  355. if (item.nickname?.indexOf(val) === -1) {
  356. item.hide = true;
  357. } else {
  358. item.hide = false;
  359. }
  360. });
  361. }}></SearchInput>
  362. </div>
  363. <NScrollbar class={styles.student}>
  364. {state.selectStudents.map((student: any) => (
  365. <div class={[styles.studentItem, student.hide && styles.hide]}>
  366. <div class={styles.studentInfo}>
  367. <NAvatar
  368. src={student.avatar || defultHeade}
  369. class={styles.studentImg}
  370. />
  371. <span class={styles.name}>{student.nickname}</span>
  372. </div>
  373. <i
  374. class={styles.iconClose}
  375. onClick={() => onRemove(student)}></i>
  376. </div>
  377. ))}
  378. {selectStudentEmpty.value && <TheEmpty />}
  379. </NScrollbar>
  380. <NSpace justify="end" class={styles.btnGroup}>
  381. <NButton type="default" onClick={() => emit('close')}>
  382. 取消
  383. </NButton>
  384. <NButton type="primary" onClick={onSave}>
  385. 保存
  386. </NButton>
  387. </NSpace>
  388. </div>
  389. </div>
  390. );
  391. }
  392. });