index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. <template>
  2. <Transition :enter-active-class="proxy?.animate.mask.enter" :leave-active-class="proxy?.animate.mask.leave">
  3. <div class="mask" v-if="optionWindow.show"></div>
  4. </Transition>
  5. <Transition :enter-active-class="proxy?.animate.dialog.enter" :leave-active-class="proxy?.animate.dialog.leave">
  6. <div class="optionWindow" v-if="optionWindow.show">
  7. <div class="box">
  8. <div class="top">
  9. <div class="title">手动选择</div>
  10. <div class="close" @click="close"></div>
  11. </div>
  12. <div class="content">
  13. <div class="searchBox">
  14. <el-select class="select" v-model="optionForm.grade" :popper-append-to-body="false" placeholder="年级"
  15. clearable>
  16. <el-option v-for="item in gradeLists" :key="item.value" :label="item.label" :value="item.value" />
  17. </el-select>
  18. <el-select class="select" v-model="optionForm.class" :popper-append-to-body="false" placeholder="班级"
  19. clearable>
  20. <el-option v-for="item in classData" :key="item.value" :label="item.name" :value="item.id" />
  21. </el-select>
  22. <el-input class="input" v-model="optionForm.name" placeholder="请输入学生姓名" clearable />
  23. <el-button class="button" type="primary" @click="getSearchStudent">搜索</el-button>
  24. </div>
  25. <div class="tableBox">
  26. <el-table ref="myTable" :data="tableData" class="table" :class="{ 'table2': props.selectType == 'single' }"
  27. @cell-click="handleCellClickChange" @row-dblclick="handleDblclick" @select-all="handleSelectAll"
  28. highlight-current-row>
  29. <el-table-column type="selection" width="55" v-if="props.selectType == 'multiple'" />
  30. <el-table-column label="头像" width="120">
  31. <template #default="scope">
  32. <el-avatar :src="scope.row.face_pic || scope.row.logo_url" />
  33. </template>
  34. </el-table-column>
  35. <el-table-column prop="className" label="班级" width="180" />
  36. <el-table-column prop="name" label="姓名" width="180" />
  37. <el-table-column prop="genderName" label="性别" />
  38. <el-table-column prop="student_number" label="学号" />
  39. </el-table>
  40. </div>
  41. <div class="page">
  42. <el-pagination layout="prev, pager, next" :total="page.total" :page-size="page.size"
  43. :page-sizes="[20, 60, 120]" :current-page="page.page" @size-change="handleSizeChange"
  44. @current-change="handleCurrentChange" />
  45. </div>
  46. <div class="multipleList" v-if="props.selectType == 'multiple'">
  47. <ul ref="scrollContainer">
  48. <li v-for="item in selectValue" :key="item.student_id">
  49. <div class="pic">
  50. <img :src="item.face_pic || item.logo_url" />
  51. <i class="del" @click="delChooseStudent(item)"></i>
  52. </div>
  53. <div>{{ item.name }}</div>
  54. </li>
  55. </ul>
  56. </div>
  57. </div>
  58. <div class="bottom">
  59. <div class="btn" @click="confirm">确定</div>
  60. </div>
  61. </div>
  62. </div>
  63. </Transition>
  64. </template>
  65. <script setup lang="ts">
  66. import useAppStore from '@/store/modules/app';
  67. import dataDictionary from "@/utils/dataDictionary"
  68. const { proxy } = getCurrentInstance() as any;
  69. const emit = defineEmits(['returnData']);
  70. const myTable = ref();
  71. const scrollContainer = ref();
  72. //父值
  73. const props = defineProps({
  74. selectType: {
  75. type: String,
  76. default: 'single',//单选single 多选multiple
  77. },
  78. });
  79. //筛选班别
  80. const classData = computed(() => {
  81. optionForm.value.class = "";
  82. let list = classList.value.filter((item: any) => {
  83. return item.grade == optionForm.value.grade;
  84. })
  85. return list;
  86. });
  87. const data = reactive<any>({
  88. optionForm: {
  89. gesture: true,
  90. hasHB: false,
  91. },
  92. optionWindow: {
  93. show: false,
  94. time: "",
  95. },
  96. classList: [],
  97. tableData: [],
  98. page: {
  99. current: 1,
  100. size: 100,
  101. total: 0,
  102. },
  103. selectValue: [],
  104. });
  105. const { optionForm, optionWindow, classList, tableData, page, selectValue } = toRefs(data);
  106. //打开
  107. const open = (data: any) => {
  108. getClass();
  109. getStudent();
  110. selectValue.value = [];
  111. optionWindow.value.show = true;
  112. };
  113. //关闭
  114. const close = () => {
  115. optionWindow.value.show = false;
  116. };
  117. //获取班级列表
  118. const getClass = () => {
  119. const list: any = useAppStore().getClass();
  120. if (list.length) {
  121. classList.value = list;
  122. console.log("classList.value", classList.value)
  123. } else {
  124. let params = {
  125. per_page: 1000,
  126. page: 1,
  127. };
  128. proxy?.$http.common.classList(params).then((res: any) => {
  129. if (res.data.length > 0) {
  130. let myList: any = res.data;
  131. classList.value = myList;
  132. useAppStore().setClass(myList);
  133. }
  134. });
  135. }
  136. };
  137. //获取学生列表
  138. const getStudent = () => {
  139. let myInfo: any = localStorage.getItem("userInfo");
  140. let userInfo = JSON.parse(myInfo);
  141. let params: any = {
  142. school_id: userInfo.school_id,
  143. class_id: optionForm.value.class,
  144. page: page.value.current,
  145. per_page: page.value.size,
  146. };
  147. if (optionForm.value.name) {
  148. params.name = optionForm.value.name;
  149. }
  150. proxy?.$http.common.studentList(params).then((res: any) => {
  151. if (res.data.length > 0) {
  152. let list = res.data.map((item: any) => {
  153. let classObj = classList.value.find((items: any) => {
  154. return items.id == item.class_id;
  155. })
  156. item.className = classObj?.name || "";
  157. item.genderName = item.gender == 1 ? "男" : "女";
  158. item.student_id = item.id;
  159. return item;
  160. });
  161. tableData.value = list;
  162. nextTick(() => {
  163. //添加点选
  164. let ids = selectValue.value.map((item: any) => {
  165. return item.student_id
  166. })
  167. list.forEach((item: any) => {
  168. if (ids.includes(item.student_id)) {
  169. myTable.value.toggleRowSelection(item, true)
  170. }
  171. })
  172. })
  173. page.value.total = res.total;
  174. }
  175. });
  176. };
  177. //搜索
  178. const getSearchStudent = () => {
  179. page.value.current = 1;
  180. getStudent();
  181. };
  182. //切换页码
  183. const handleCurrentChange = (data: number) => {
  184. page.value.current = data;
  185. getStudent();
  186. };
  187. //切换页数
  188. const handleSizeChange = (data: number) => {
  189. page.value.current = 1;
  190. page.value.size = data;
  191. getStudent();
  192. };
  193. //单选行
  194. const handleCellClickChange = (data: any) => {
  195. if (props.selectType == 'single') {
  196. selectValue.value = [data];
  197. }
  198. if (props.selectType == 'multiple') {
  199. let ids = selectValue.value.map((item: any) => {
  200. return item.student_id;
  201. })
  202. if (!ids.includes(data.student_id)) {
  203. //不在内就添加
  204. myTable.value.toggleRowSelection(data, true)
  205. selectValue.value.push(data);
  206. nextTick(() => {
  207. scrollContainer.value.scrollLeft = scrollContainer.value.scrollWidth;
  208. });
  209. } else {
  210. //在内就移除
  211. myTable.value.toggleRowSelection(data, false)
  212. selectValue.value = selectValue.value.filter((item: any) => {
  213. return item.student_id != data.student_id
  214. })
  215. }
  216. }
  217. };
  218. //全选/不选操作
  219. const handleSelectAll = (data: any) => {
  220. selectValue.value = data;
  221. };
  222. //双击
  223. const handleDblclick = (data: any) => {
  224. if (props.selectType == 'single') {
  225. confirm();
  226. }
  227. };
  228. //删除已选学生
  229. const delChooseStudent = (data: any) => {
  230. selectValue.value = selectValue.value.filter((item: any) => {
  231. return item.student_id != data.student_id
  232. })
  233. //表格勾选也移除
  234. myTable.value.toggleRowSelection(data, false)
  235. };
  236. //确定
  237. const confirm = () => {
  238. console.log("selectValue.value", selectValue.value)
  239. if (selectValue.value.length == 0) {
  240. proxy?.$modal.msgWarning(`请选择!`);
  241. return false;
  242. }
  243. if (props.selectType == 'multiple') {
  244. emit('returnData', selectValue.value);
  245. } else {
  246. emit('returnData', selectValue.value[0]);
  247. }
  248. close();
  249. };
  250. //年级
  251. const gradeLists = computed(() => {
  252. let myInfo: any = localStorage.getItem("userInfo");
  253. let userInfo = JSON.parse(myInfo);
  254. let obj = dataDictionary.gradeLists.find((item) => {
  255. return userInfo.category == item.value
  256. })
  257. return obj != undefined ? obj.child : [];
  258. });
  259. onMounted(() => {
  260. })
  261. //暴露给父组件用
  262. defineExpose({
  263. open,
  264. close,
  265. optionWindow
  266. })
  267. </script>
  268. <style lang="scss" scoped>
  269. .mask {
  270. position: fixed;
  271. height: 100vh;
  272. width: 100vw;
  273. top: 0;
  274. left: 0;
  275. background: rgba(0, 0, 0, 0.3);
  276. z-index: 998;
  277. }
  278. .optionWindow {
  279. position: fixed;
  280. height: 100vh;
  281. width: 100vw;
  282. top: 0;
  283. left: 0;
  284. display: flex;
  285. justify-content: center;
  286. align-items: center;
  287. color: #FFFFFF;
  288. z-index: 999;
  289. .box {
  290. width: 50%;
  291. border-radius: 1.6rem;
  292. background: linear-gradient(60deg, #092941 -85%, #2A484B 96%);
  293. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.3);
  294. padding: 20px;
  295. .top {
  296. color: #ffffff;
  297. display: flex;
  298. justify-content: space-between;
  299. align-items: center;
  300. margin-bottom: 20px;
  301. .title {
  302. font-size: 1.93rem;
  303. color: #13ED84;
  304. }
  305. .close {}
  306. }
  307. .content {
  308. margin-bottom: 20px;
  309. .searchBox {
  310. margin-bottom: 10px;
  311. display: flex;
  312. justify-content: space-between;
  313. .select,
  314. .input {
  315. width: 27%;
  316. }
  317. .el-select__wrapper {
  318. border-radius: 15px;
  319. color: #1A293A;
  320. background: radial-gradient(30% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%) !important;
  321. }
  322. .el-input__wrapper {
  323. width: 25%;
  324. background: #ffffff;
  325. border-radius: 15px;
  326. }
  327. .button {
  328. color: #1A293A;
  329. background: radial-gradient(141% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
  330. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  331. border: none;
  332. }
  333. }
  334. .tableBox {
  335. .table {
  336. height: 52vh;
  337. border-radius: 1.6rem;
  338. margin-bottom: 10px;
  339. overflow: hidden;
  340. }
  341. .el-table__body tr {
  342. cursor: pointer;
  343. }
  344. .el-table__body tr.current-row>td.el-table__cell {
  345. color: #1A293A;
  346. background: #07FFE7;
  347. }
  348. }
  349. .page {
  350. .el-pagination {
  351. display: flex;
  352. justify-content: center;
  353. .el-pager {
  354. margin: 0 10px !important;
  355. background: #D8D8D8;
  356. box-sizing: border-box;
  357. border: 1px solid #979797;
  358. border-radius: 15px;
  359. overflow: hidden;
  360. }
  361. .btn-prev,
  362. .btn-next {
  363. border-radius: 50%;
  364. background: radial-gradient(116% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  365. }
  366. }
  367. }
  368. .multipleList {
  369. ul {
  370. display: flex;
  371. overflow-x: scroll;
  372. padding: 15px 0 0px 0;
  373. li {
  374. margin-right: 20px;
  375. text-align: center;
  376. flex-shrink: 0;
  377. .pic {
  378. width: 40px;
  379. height: 40px;
  380. position: relative;
  381. border-radius: 50%;
  382. display: flex;
  383. justify-content: center;
  384. align-items: center;
  385. overflow: hidden;
  386. box-sizing: border-box;
  387. border: 1px solid rgba(255, 255, 255, 0.5);
  388. margin: 0 auto 5px auto;
  389. font-size: 12px;
  390. img {
  391. width: 100%;
  392. }
  393. .del {
  394. width: 100%;
  395. height: 100%;
  396. position: absolute;
  397. z-index: 2;
  398. right: 0;
  399. top: 0;
  400. box-sizing: border-box;
  401. border: 1px solid #979797;
  402. background-image: url("@/assets/images/common/close.png");
  403. background-position: center;
  404. background-repeat: no-repeat;
  405. background-size: 30% 30%;
  406. background-color: rgba(255, 255, 255, 0.5);
  407. border-radius: 50%;
  408. transition: all 0.5s;
  409. cursor: pointer;
  410. display: none;
  411. }
  412. }
  413. &:hover {
  414. .del {
  415. display: block;
  416. }
  417. }
  418. }
  419. &::-webkit-scrollbar {
  420. width: 6px;
  421. }
  422. &::-webkit-scrollbar-thumb {
  423. border-width: 2px;
  424. border-radius: 3px;
  425. border-style: dashed;
  426. border-color: transparent;
  427. background: rgb(27, 72, 92);
  428. background-clip: padding-box;
  429. }
  430. &::-webkit-scrollbar-button:hover {
  431. border-radius: 5px;
  432. background: rgba(26, 62, 78, 1);
  433. }
  434. }
  435. }
  436. }
  437. .bottom {
  438. height: 3.59rem;
  439. align-items: center;
  440. color: #ffffff;
  441. display: flex;
  442. justify-content: space-around;
  443. .btn {
  444. width: 33%;
  445. height: 3.59rem;
  446. line-height: 3.59rem;
  447. font-size: 2rem;
  448. margin: 0 auto;
  449. border-radius: 0.83rem;
  450. font-size: 2rem;
  451. text-align: center;
  452. color: #1A293A;
  453. background: radial-gradient(141% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
  454. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  455. cursor: pointer;
  456. }
  457. }
  458. }
  459. }
  460. ::v-deep(.table .el-table__body tr:hover>td.el-table__cell) {
  461. background: #dddddd;
  462. cursor: pointer;
  463. -webkit-user-select: none;
  464. -moz-user-select: none;
  465. -ms-user-select: none;
  466. user-select: none;
  467. }
  468. ::v-deep(.table .el-table__body-wrapper .el-table-column--selection .el-checkbox) {
  469. pointer-events: none;
  470. }
  471. ::v-deep(.table2 .el-table__body tr.current-row > td) {
  472. background: #13ED84 !important;
  473. color: #ffffff !important;
  474. }
  475. </style>