student.vue 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457
  1. <template>
  2. <section class="content">
  3. <div class="box box-info">
  4. <!-- 正文信息 -->
  5. <div class="box-body">
  6. <el-form
  7. ref="formSearch"
  8. :model="formSearch"
  9. :inline="true"
  10. label-width="70px"
  11. >
  12. <el-form-item v-if="isSuperAdmin" label="学校">
  13. <el-select
  14. v-model="formSearch.rootOrgId"
  15. placeholder="请选择"
  16. class="input"
  17. @change="rootOrgChanged"
  18. >
  19. <el-option
  20. v-for="item in rootOrgList"
  21. :key="item.id"
  22. :label="item.name"
  23. :value="item.id"
  24. ></el-option>
  25. </el-select>
  26. </el-form-item>
  27. <el-form-item label="姓名">
  28. <el-input
  29. v-model="formSearch.name"
  30. placeholder="请输入姓名"
  31. class="input"
  32. ></el-input>
  33. </el-form-item>
  34. <el-form-item label="学号">
  35. <el-input
  36. v-model="formSearch.studentCode"
  37. placeholder="请输入学号"
  38. class="input"
  39. ></el-input>
  40. </el-form-item>
  41. <el-form-item label="身份证">
  42. <el-input
  43. v-model="formSearch.identityNumber"
  44. placeholder="请输入身份证"
  45. class="input"
  46. ></el-input>
  47. </el-form-item>
  48. <el-form-item label="学习中心">
  49. <el-select
  50. v-model="formSearch.orgId"
  51. class="input"
  52. :remote-method="getOrgList4Search"
  53. :loading="getOrgList4SearchLoading"
  54. remote
  55. filterable
  56. clearable
  57. placeholder="请选择"
  58. >
  59. <el-option
  60. v-for="item in orgList4Search"
  61. :key="item.id"
  62. :label="item.name + ' - ' + item.code"
  63. :value="item.id"
  64. ></el-option>
  65. </el-select>
  66. </el-form-item>
  67. <el-form-item label="照片状态">
  68. <el-select v-model="formSearch.hasPhoto" class="input">
  69. <el-option label="未选择" value="UNDEFINED"></el-option>
  70. <el-option label="已上传" value="TRUE"></el-option>
  71. <el-option label="未上传" value="FALSE"></el-option>
  72. </el-select>
  73. </el-form-item>
  74. <el-form-item label="状态">
  75. <el-select v-model="formSearch.enable" class="input">
  76. <el-option label="未选择" value="UNDEFINED"></el-option>
  77. <el-option label="已启用" value="TRUE"></el-option>
  78. <el-option label="已禁用" value="FALSE"></el-option>
  79. </el-select>
  80. </el-form-item>
  81. <el-form-item class="d-block">
  82. <el-button
  83. v-if="rolePrivileges.search_student"
  84. size="small"
  85. type="primary"
  86. icon="el-icon-search"
  87. @click="resetPageAndSearchForm"
  88. >查询</el-button
  89. >
  90. <el-button
  91. size="small"
  92. icon="el-icon-refresh"
  93. @click="resetSearchForm"
  94. >重置</el-button
  95. >
  96. <el-button
  97. v-if="rolePrivileges.upload_student_photo"
  98. size="small"
  99. type="primary"
  100. icon="el-icon-upload2"
  101. @click="uploadPhoto"
  102. >上传照片</el-button
  103. >
  104. <el-button
  105. size="small"
  106. type="primary"
  107. icon="el-icon-download"
  108. @click="exportStudent"
  109. >导出</el-button
  110. >
  111. </el-form-item>
  112. </el-form>
  113. <div class="block-seperator"></div>
  114. <span>批量操作:</span>
  115. <el-button
  116. v-if="rolePrivileges.reset_student_password"
  117. size="small"
  118. type="danger"
  119. icon="el-icon-refresh"
  120. :disabled="noBatchSelected"
  121. @click="resetPassword('')"
  122. >重置密码</el-button
  123. >
  124. <el-button
  125. v-if="rolePrivileges.change_student_availability"
  126. size="small"
  127. type="success"
  128. icon="el-icon-check"
  129. :disabled="noBatchSelected"
  130. @click="enableStudent('')"
  131. >启用</el-button
  132. >
  133. <el-button
  134. v-if="rolePrivileges.change_student_availability"
  135. size="small"
  136. type="danger"
  137. icon="el-icon-close"
  138. :disabled="noBatchSelected"
  139. @click="disableStudent('')"
  140. >禁用</el-button
  141. >
  142. <el-button
  143. v-if="rolePrivileges.reset_student_password"
  144. size="small"
  145. type="danger"
  146. icon="el-icon-refresh"
  147. @click="resetPasswordByOrgId()"
  148. >重置学习中心所有学生密码</el-button
  149. >
  150. <div style="width: 100%; margin-bottom: 10px"></div>
  151. <!-- 重置学习中心所有学生密码-->
  152. <el-dialog
  153. title="重置学习中心所有学生密码"
  154. width="500px"
  155. :visible.sync="resetPasswordByOrgIdDialog"
  156. >
  157. <el-form
  158. ref="resetPasswordByOrgIdForm"
  159. :model="resetPasswordByOrgIdForm"
  160. :inline="true"
  161. label-width="80px"
  162. :rules="resetPasswordByOrgIdRules"
  163. >
  164. <el-form-item label="学习中心" prop="orgId">
  165. <el-select
  166. v-model="resetPasswordByOrgIdForm.orgId"
  167. class="input"
  168. :remote-method="getOrgList4RestPassword"
  169. :loading="getOrgList4RestPasswordLoading"
  170. remote
  171. filterable
  172. clearable
  173. placeholder="请选择"
  174. >
  175. <el-option
  176. v-for="item in orgList4RestPassword"
  177. :key="item.id"
  178. :label="item.name + ' - ' + item.code"
  179. :value="item.id"
  180. ></el-option>
  181. </el-select>
  182. </el-form-item>
  183. </el-form>
  184. <div style="text-align: center; margin-top: 20px">
  185. <el-button type="primary" @click="submitResetPasswordByOrgId"
  186. >确 定</el-button
  187. >
  188. <el-button @click="resetPasswordByOrgIdDialog = false"
  189. >取 消</el-button
  190. >
  191. </div>
  192. </el-dialog>
  193. <!-- 页面列表 -->
  194. <el-table
  195. :data="tableData"
  196. border
  197. style="width: 100%; text-align: center"
  198. @selection-change="selectChange"
  199. >
  200. <el-table-column type="selection" width="50"></el-table-column>
  201. <el-table-column
  202. prop="id"
  203. width="100"
  204. label="ID"
  205. sortable
  206. ></el-table-column>
  207. <el-table-column label="考生" width="100">
  208. <template slot-scope="scope">
  209. <el-popover trigger="hover" placement="left">
  210. <div style="font-size: 18px; font-family: 新宋体">
  211. <tr>
  212. <td style="color: green">姓名</td>
  213. <td style="color: purple; padding-left: 20px">
  214. {{ scope.row.name }}
  215. </td>
  216. </tr>
  217. <tr>
  218. <td style="color: green">身份证号</td>
  219. <td style="color: purple; padding-left: 20px">
  220. {{ scope.row.privateIdentityNumber }}
  221. </td>
  222. </tr>
  223. <tr>
  224. <td style="color: green">学号</td>
  225. <td style="color: purple; padding-left: 20px">
  226. {{ scope.row.studentCodeList }}
  227. </td>
  228. </tr>
  229. <tr>
  230. <td style="color: green">学习中心名称</td>
  231. <td style="color: purple; padding-left: 20px">
  232. {{ scope.row.orgName }}
  233. </td>
  234. </tr>
  235. <tr>
  236. <td style="color: green">学习中心编码</td>
  237. <td style="color: purple; padding-left: 20px">
  238. {{ scope.row.orgCode }}
  239. </td>
  240. </tr>
  241. <tr>
  242. <td style="color: green">手机号</td>
  243. <td style="color: purple; padding-left: 20px">
  244. {{ scope.row.phoneNumber }}
  245. </td>
  246. </tr>
  247. <tr>
  248. <td style="color: green">安全手机号</td>
  249. <td style="color: purple; padding-left: 20px">
  250. {{ scope.row.securityPhone }}
  251. </td>
  252. </tr>
  253. <tr>
  254. <td style="color: green">创建时间</td>
  255. <td style="color: purple; padding-left: 20px">
  256. {{ scope.row.creationTime }}
  257. </td>
  258. </tr>
  259. </div>
  260. <div slot="reference" class="name-wrapper">
  261. <span>{{ scope.row.name }}</span>
  262. </div>
  263. </el-popover>
  264. </template>
  265. </el-table-column>
  266. <el-table-column label="学号">
  267. <span slot-scope="scope" v-html="scope.row.studentCodesStr"></span>
  268. </el-table-column>
  269. <el-table-column
  270. prop="privateIdentityNumber"
  271. width
  272. label="身份证"
  273. sortable
  274. ></el-table-column>
  275. <el-table-column
  276. prop="orgName"
  277. width
  278. label="学习中心"
  279. sortable
  280. ></el-table-column>
  281. <el-table-column width="168" label="更新时间" sortable>
  282. <template slot-scope="scope">
  283. <el-button type="text" @click="gotoOperateLog(scope.row.id)">{{
  284. scope.row.updateTime
  285. }}</el-button>
  286. </template>
  287. </el-table-column>
  288. <el-table-column width="50" label="状态">
  289. <span slot-scope="scope">
  290. <span v-if="scope.row.enable">
  291. <el-tooltip
  292. class="item"
  293. effect="dark"
  294. content="启用"
  295. placement="left"
  296. >
  297. <i class="el-icon-success" style="color: green"></i>
  298. </el-tooltip>
  299. </span>
  300. <span v-else>
  301. <el-tooltip
  302. class="item"
  303. effect="dark"
  304. content="禁用"
  305. placement="left"
  306. >
  307. <i class="el-icon-error" style="color: red"></i>
  308. </el-tooltip>
  309. </span>
  310. </span>
  311. </el-table-column>
  312. <el-table-column label="操作" width="300">
  313. <div slot-scope="scope">
  314. <el-button
  315. :disabled="!scope.row.photoPath"
  316. size="mini"
  317. type="primary"
  318. plain
  319. icon="el-icon-picture"
  320. @click="showPhoto(scope.row)"
  321. >查看照片</el-button
  322. >
  323. <el-button
  324. v-if="
  325. null != scope.row.enable &&
  326. !scope.row.enable &&
  327. rolePrivileges.change_student_availability
  328. "
  329. size="mini"
  330. type="primary"
  331. plain
  332. icon="el-icon-check"
  333. @click="enableStudent(scope.row)"
  334. >启用</el-button
  335. >
  336. <el-button
  337. v-else-if="rolePrivileges.change_student_availability"
  338. size="mini"
  339. type="danger"
  340. icon="el-icon-close"
  341. @click="disableStudent(scope.row)"
  342. >禁用</el-button
  343. >
  344. <el-dropdown style="margin-left: 10px">
  345. <el-button type="primary" plain size="mini">
  346. 更多
  347. <i class="el-icon-arrow-down el-icon--right"></i>
  348. </el-button>
  349. <el-dropdown-menu slot="dropdown">
  350. <el-dropdown-item
  351. v-if="rolePrivileges.reset_student_password"
  352. >
  353. <el-button
  354. size="mini"
  355. type="danger"
  356. icon="el-icon-refresh"
  357. @click="resetPassword(scope.row)"
  358. >重置密码</el-button
  359. >
  360. </el-dropdown-item>
  361. <el-dropdown-item>
  362. <el-button
  363. size="mini"
  364. type="primary"
  365. icon="el-icon-document"
  366. @click="showStuExamDialog(scope.row)"
  367. >考试记录</el-button
  368. >
  369. </el-dropdown-item>
  370. <el-dropdown-item v-if="rolePrivileges.unbind_student_code">
  371. <el-button
  372. size="mini"
  373. type="danger"
  374. icon="el-icon-refresh"
  375. @click="showUnbindStudentCode(scope.row)"
  376. >解绑学号</el-button
  377. >
  378. </el-dropdown-item>
  379. <el-dropdown-item v-if="rolePrivileges.unbind_security_phone">
  380. <el-button
  381. size="mini"
  382. type="danger"
  383. icon="el-icon-refresh"
  384. @click="unbindSecurityPhone(scope.row)"
  385. >解绑安全手机</el-button
  386. >
  387. </el-dropdown-item>
  388. </el-dropdown-menu>
  389. </el-dropdown>
  390. </div>
  391. </el-table-column>
  392. </el-table>
  393. <div class="page pull-right">
  394. <el-pagination
  395. :current-page="currentPage"
  396. :page-size="pageSize"
  397. :page-sizes="[10, 20, 50, 100, 200, 300]"
  398. layout="total, sizes, prev, pager, next, jumper"
  399. :total="total"
  400. @current-change="handleCurrentChange"
  401. @size-change="handleSizeChange"
  402. ></el-pagination>
  403. </div>
  404. <!-- 解绑学号-->
  405. <el-dialog
  406. title="解绑学号"
  407. width="50%"
  408. :visible.sync="unbindStudentCodeDialog"
  409. >
  410. <el-table
  411. :data="unbindStudentCodeData.tableData"
  412. border
  413. style="width: 100%; text-align: center"
  414. >
  415. <el-table-column prop="name" label="姓名" />
  416. <el-table-column prop="identityNumber" label="身份证" />
  417. <el-table-column prop="studentCode" label="学号" />
  418. <el-table-column label="操作" width="100">
  419. <div slot-scope="scope">
  420. <el-button
  421. size="mini"
  422. type="danger"
  423. icon="el-icon-delete"
  424. @click="unbindStudentCode(scope.row)"
  425. >解绑</el-button
  426. >
  427. </div>
  428. </el-table-column>
  429. </el-table>
  430. </el-dialog>
  431. <!-- 考试记录 -->
  432. <el-dialog
  433. v-loading="stuExamLoading"
  434. title="学生考试记录"
  435. width="60%"
  436. :visible.sync="stuExamDialog"
  437. element-loading-text="拼命加载中"
  438. >
  439. <el-form
  440. :inline="true"
  441. :model="stuExamSearch"
  442. label-position="right"
  443. label-width="50px"
  444. >
  445. <el-form-item label="考试" class="pull-left">
  446. <el-select
  447. v-model="stuExamSearch.examId"
  448. class="input"
  449. :remote-method="queryExams4Search"
  450. remote
  451. :loading="queryExams4SearchLoading"
  452. filterable
  453. clearable
  454. placeholder="请选择"
  455. @change="handleExamChange4Search"
  456. >
  457. <el-option
  458. v-for="item in examList4Search"
  459. :key="item.id"
  460. :label="item.name"
  461. :value="item.id"
  462. ></el-option>
  463. </el-select>
  464. </el-form-item>
  465. <el-form-item label="场次" class="pull-left">
  466. <el-select
  467. v-model="stuExamSearch.examStageId"
  468. clearable
  469. :disabled="examStageDisabled4Search"
  470. class="input"
  471. :remote-method="queryExamStages4Search"
  472. remote
  473. :loading="queryExamStages4SearchLoading"
  474. :filterable="true"
  475. placeholder="请选择"
  476. >
  477. <el-option
  478. v-for="item in examStageList4Search"
  479. :key="item.id"
  480. :label="item.stageOrder"
  481. :value="item.id"
  482. ></el-option>
  483. </el-select>
  484. </el-form-item>
  485. <el-form-item class="pull-left">
  486. <el-button
  487. size="small"
  488. type="primary"
  489. icon="el-icon-search"
  490. @click="searchExamRecord"
  491. >查询</el-button
  492. >
  493. </el-form-item>
  494. </el-form>
  495. <el-table
  496. :data="stuExamList"
  497. border
  498. style="width: 100%; text-align: center"
  499. >
  500. <el-table-column
  501. prop="studentName"
  502. label="学生姓名"
  503. ></el-table-column>
  504. <el-table-column prop="studentCode" label="学号"></el-table-column>
  505. <el-table-column prop="ext2" label="身份证"></el-table-column>
  506. <el-table-column
  507. prop="examName"
  508. label="考试"
  509. sortable
  510. ></el-table-column>
  511. <el-table-column
  512. v-if="!examStageDisabled4Search"
  513. prop="examStageOrder"
  514. label="场次"
  515. width="120"
  516. sortable
  517. ></el-table-column>
  518. <el-table-column
  519. prop="courseName"
  520. label="课程"
  521. sortable
  522. ></el-table-column>
  523. <el-table-column width="130" label="考试状态">
  524. <div slot-scope="scope">
  525. <span v-if="null == scope.row.started"></span>
  526. <span v-else-if="scope.row.started">已考</span>
  527. <span v-else>未考</span>
  528. </div>
  529. </el-table-column>
  530. </el-table>
  531. <div class="page pull-right">
  532. <el-pagination
  533. :current-page="stuExamCurPage"
  534. :page-size="stuExamPageSize"
  535. :page-sizes="[10, 20, 50, 100, 200, 300]"
  536. layout="total, sizes, prev, pager, next, jumper"
  537. :total="stuExamTotal"
  538. @current-change="stuExamCurChange"
  539. @size-change="handleStuExamSizeChange"
  540. ></el-pagination>
  541. </div>
  542. <div style="margin-top: 10px"></div>
  543. </el-dialog>
  544. <!-- 导入照片弹窗 -->
  545. <el-dialog
  546. title="学生照片导入"
  547. width="350px"
  548. :visible.sync="photoUploadDialog"
  549. >
  550. <el-form>
  551. <el-row>
  552. <el-form-item style="margin-left: 30px">
  553. <el-upload
  554. ref="upload"
  555. class="form_left"
  556. list-type="picture"
  557. :action="uploadAction"
  558. :headers="uploadHeaders"
  559. :data="uploadData"
  560. :before-upload="beforeUpload"
  561. :on-progress="uploadProgress"
  562. :on-success="uploadSuccess"
  563. :on-error="uploadError"
  564. :file-list="fileList"
  565. :auto-upload="false"
  566. :multiple="false"
  567. >
  568. <el-button
  569. slot="trigger"
  570. size="small"
  571. type="primary"
  572. icon="el-icon-search"
  573. >选择文件</el-button
  574. >
  575. <el-button
  576. size="small"
  577. style="margin-left: 10px"
  578. type="primary"
  579. icon="el-icon-check"
  580. @click="submitUpload"
  581. >确认上传</el-button
  582. >
  583. <div slot="tip" class="el-upload__tip">
  584. 只能上传jpg,png文件
  585. </div>
  586. </el-upload>
  587. </el-form-item>
  588. </el-row>
  589. </el-form>
  590. </el-dialog>
  591. <!--查看照片-->
  592. <el-dialog
  593. title="照片"
  594. :visible.sync="photoDialog"
  595. width="300px"
  596. :center="true"
  597. @close="closePhotoDialog"
  598. >
  599. <img :src="photo.url" height="100%" width="100%" />
  600. </el-dialog>
  601. <el-dialog
  602. v-loading="studentLog.loading"
  603. title="学生日志"
  604. width="60%"
  605. :visible.sync="stuLogDialog"
  606. :close-on-click-modal="false"
  607. element-loading-text="拼命加载中"
  608. >
  609. <!-- 表单 -->
  610. <el-form inline :model="studentLog.formSearch">
  611. <el-form-item label="操作内容">
  612. <el-input
  613. v-model="studentLog.formSearch.operate"
  614. placeholder="请输入操作内容"
  615. style="width: 180px"
  616. />
  617. </el-form-item>
  618. <el-form-item label="操作时间">
  619. <el-date-picker
  620. v-model="studentLog.timeRange"
  621. class="input"
  622. type="datetimerange"
  623. start-placeholder="开始日期"
  624. range-separator="至"
  625. end-placeholder="结束日期"
  626. value-format="yyyy/MM/dd HH:mm:ss"
  627. :clearable="false"
  628. size="small"
  629. @change="changeTimeRange"
  630. ></el-date-picker>
  631. </el-form-item>
  632. <el-form-item>
  633. <el-button
  634. size="small"
  635. type="primary"
  636. icon="el-icon-search"
  637. @click="loghandleSearchBtn"
  638. >查询</el-button
  639. >
  640. </el-form-item>
  641. </el-form>
  642. <div class="block-seperator"></div>
  643. <!-- 页面列表 -->
  644. <el-table
  645. :data="studentLog.tableData"
  646. border
  647. resizable
  648. stripe
  649. style="width: 100%"
  650. >
  651. <el-table-column
  652. width="200"
  653. prop="operateTime"
  654. label="操作时间"
  655. ></el-table-column>
  656. <el-table-column
  657. width="200"
  658. prop="operateIp"
  659. label="IP"
  660. ></el-table-column>
  661. <el-table-column
  662. width="200"
  663. prop="operateClient"
  664. label="操作端"
  665. ></el-table-column>
  666. <el-table-column prop="operate" label="操作内容"></el-table-column>
  667. </el-table>
  668. <div class="page pull-right">
  669. <el-pagination
  670. v-if="studentLog.paginationShow"
  671. :current-page="studentLog.currentPage"
  672. :page-size="studentLog.pageSize"
  673. :page-sizes="[10, 20, 50, 100, 200, 300]"
  674. layout="total, sizes, prev, pager, next, jumper"
  675. :total="studentLog.total"
  676. @current-change="loghandleCurrentChange"
  677. @size-change="loghandleSizeChange"
  678. />
  679. </div>
  680. </el-dialog>
  681. </div>
  682. </div>
  683. </section>
  684. </template>
  685. <script>
  686. import {
  687. CORE_API,
  688. EXAM_WORK_API,
  689. EXCHANGE_API,
  690. REPORTS_API,
  691. } from "@/constants/constants.js";
  692. import { mapState } from "vuex";
  693. export default {
  694. data() {
  695. return {
  696. rolePrivileges: {
  697. search_student: false,
  698. upload_student_photo: false,
  699. reset_student_password: false,
  700. change_student_availability: false,
  701. unbind_student_code: false,
  702. unbind_security_phone: false,
  703. },
  704. rootOrgList: null,
  705. stuExamLoading: false,
  706. button: {},
  707. pureLC: false,
  708. lc_id: null,
  709. lc_code: null,
  710. lc_name: null,
  711. orgList4Search: [],
  712. orgList4RestPassword: [],
  713. getOrgList4SearchLoading: false,
  714. getOrgList4RestPasswordLoading: false,
  715. resetPasswordByOrgIdDialog: false,
  716. resetPasswordByOrgIdForm: {
  717. orgId: "",
  718. },
  719. formSearch: {
  720. orgId: "",
  721. rootOrgId: null,
  722. name: "",
  723. studentCode: "",
  724. identityNumber: "",
  725. enable: "UNDEFINED",
  726. hasPhoto: "UNDEFINED",
  727. },
  728. selectedStuIds: [],
  729. statusList: [
  730. {
  731. value: true,
  732. label: "启用",
  733. },
  734. {
  735. value: false,
  736. label: "禁用",
  737. },
  738. ],
  739. tableData: [],
  740. currentPage: 1,
  741. pageSize: 10,
  742. total: 10,
  743. photoDialog: false,
  744. stuLogDialog: false,
  745. photo: { url: "" },
  746. unbindStudentCodeDialog: false,
  747. unbindStudentCodeData: {
  748. tableData: [],
  749. },
  750. errMessages: [],
  751. uploadAction: EXCHANGE_API + "/facepp/add",
  752. uploadData: {},
  753. fileLoading: false,
  754. fileList: [],
  755. uploadHeaders: {},
  756. photoUploadDialog: false,
  757. stuExamCurPage: 1,
  758. stuExamPageSize: 10,
  759. stuExamTotal: 10,
  760. stuExamDialog: false,
  761. stuExamList: [],
  762. examList4Search: [],
  763. queryExams4SearchLoading: false,
  764. stuExamSearch: {
  765. rootOrgId: "",
  766. examId: "",
  767. examStageId: "",
  768. studentCode: "",
  769. studentName: "",
  770. orgId: "",
  771. specialtyName: "",
  772. courseCode: "",
  773. courseName: "",
  774. infoCollector: "",
  775. identityNumber: "",
  776. identityNumberLike: false,
  777. },
  778. resetPasswordByOrgIdRules: {
  779. orgId: [
  780. {
  781. type: "number",
  782. required: true,
  783. message: "请选择学习中心",
  784. trigger: "change",
  785. },
  786. ],
  787. },
  788. examStageDisabled4Search: true,
  789. queryExamStages4SearchLoading: false,
  790. examStageList4Search: [],
  791. studentLog: {
  792. loading: false,
  793. timeRange: [],
  794. paginationShow: false,
  795. formSearch: {
  796. operate: null,
  797. studentId: "",
  798. startTime: null,
  799. endTime: null,
  800. },
  801. tableData: [],
  802. currentPage: 1,
  803. pageSize: 10,
  804. total: 10,
  805. },
  806. };
  807. },
  808. computed: {
  809. ...mapState({ user: (state) => state.user }),
  810. stuIds() {
  811. var stuIds = "";
  812. for (let stuId of this.selectedStuIds) {
  813. if (!stuIds) {
  814. stuIds += stuId;
  815. } else {
  816. stuIds += "," + stuId;
  817. }
  818. }
  819. return stuIds;
  820. },
  821. noBatchSelected() {
  822. return this.selectedStuIds.length === 0;
  823. },
  824. isSuperAdmin() {
  825. return this.user.roleList.some((role) => role.roleCode == "SUPER_ADMIN");
  826. },
  827. },
  828. created() {
  829. this.init();
  830. this.uploadHeaders = {
  831. key: this.user.key,
  832. token: this.user.token,
  833. };
  834. },
  835. methods: {
  836. gotoOperateLog(studentId) {
  837. this.studentLog.formSearch.operate = null;
  838. this.studentLog.timeRange = [];
  839. this.studentLog.formSearch.startTime = null;
  840. this.studentLog.formSearch.endTime = null;
  841. this.studentLog.formSearch.studentId = studentId;
  842. this.studentLog.tableData = [];
  843. this.studentLog.total = 0;
  844. this.studentLog.currentPage = 1;
  845. this.stuLogDialog = true;
  846. this.logsearchForm();
  847. },
  848. changeTimeRange(e) {
  849. if (e && e.length > 0) {
  850. this.studentLog.formSearch.startTime = e[0];
  851. this.studentLog.formSearch.endTime = e[1];
  852. } else {
  853. this.studentLog.formSearch.startTime = "";
  854. this.studentLog.formSearch.endTime = "";
  855. }
  856. },
  857. loghandleSearchBtn() {
  858. this.studentLog.currentPage = 1;
  859. this.logsearchForm();
  860. },
  861. loghandleSizeChange(val) {
  862. this.studentLog.pageSize = val;
  863. this.studentLog.currentPage = 1;
  864. this.logsearchForm();
  865. },
  866. loghandleCurrentChange(val) {
  867. this.studentLog.currentPage = val;
  868. this.logsearchForm();
  869. },
  870. //查询
  871. logsearchForm() {
  872. this.studentLog.loading = true;
  873. var url =
  874. REPORTS_API +
  875. "/studentOperate/page/" +
  876. this.studentLog.currentPage +
  877. "/" +
  878. this.studentLog.pageSize;
  879. this.$httpWithMsg
  880. .get(url, { params: this.studentLog.formSearch })
  881. .then((response) => {
  882. this.studentLog.tableData = response.data.list;
  883. this.studentLog.total = response.data.total;
  884. this.studentLog.loading = false;
  885. this.$nextTick(function () {
  886. this.studentLog.paginationShow = true;
  887. });
  888. })
  889. .finally(() => (this.studentLog.loading = false));
  890. },
  891. resetPasswordByOrgId() {
  892. this.orgList4RestPassword = [];
  893. this.resetPasswordByOrgIdForm.orgId = null;
  894. this.getOrgList4RestPassword("");
  895. if (this.pureLC) {
  896. // this.orgList4RestPassword = [
  897. // { id: this.lc_id, name: this.lc_name, code: this.lc_code },
  898. // ];
  899. // this.resetPasswordByOrgIdForm.orgId = this.lc_id;
  900. }
  901. this.resetPasswordByOrgIdDialog = true;
  902. },
  903. submitResetPasswordByOrgId() {
  904. this.$refs.resetPasswordByOrgIdForm.validate((valid) => {
  905. if (valid) {
  906. var url =
  907. CORE_API +
  908. "/student/resetPasswordByOrgId/" +
  909. this.resetPasswordByOrgIdForm.orgId;
  910. this.$httpWithMsg.put(url).then((response) => {
  911. console.log(response);
  912. this.$notify({
  913. type: "success",
  914. message: "重置密码成功",
  915. });
  916. this.searchForm();
  917. this.resetPasswordByOrgIdDialog = false;
  918. });
  919. } else {
  920. return false;
  921. }
  922. });
  923. },
  924. rootOrgChanged() {
  925. this.getOrgList4Search("");
  926. },
  927. getOrgList4Search(orgName) {
  928. this.getOrgList4SearchLoading = true;
  929. let url =
  930. CORE_API +
  931. "/org/query?rootOrgId=" +
  932. this.formSearch.rootOrgId +
  933. "&name=" +
  934. orgName;
  935. this.$httpWithMsg
  936. .get(url)
  937. .then((response) => {
  938. this.getOrgList4SearchLoading = false;
  939. this.orgList4Search = response.data;
  940. })
  941. .catch((response) => {
  942. console.log(response);
  943. this.getOrgList4SearchLoading = false;
  944. });
  945. },
  946. getOrgList4RestPassword(orgName) {
  947. this.getOrgList4RestPasswordLoading = true;
  948. let url =
  949. CORE_API +
  950. "/org/query?rootOrgId=" +
  951. this.formSearch.rootOrgId +
  952. "&name=" +
  953. orgName;
  954. this.$httpWithMsg
  955. .get(url)
  956. .then((response) => {
  957. this.getOrgList4RestPasswordLoading = false;
  958. this.orgList4RestPassword = response.data;
  959. })
  960. .catch((response) => {
  961. console.log(response);
  962. this.getOrgList4RestPasswordLoading = false;
  963. });
  964. },
  965. queryExams4Search(name) {
  966. this.queryExams(name, "search");
  967. },
  968. queryExams(name, where) {
  969. console.log("queryExams; name: " + name);
  970. if ("search" == where) {
  971. this.queryExams4SearchLoading = true;
  972. }
  973. this.$httpWithMsg
  974. .get(EXAM_WORK_API + "/exam/queryByNameLike?enable=true&name=" + name)
  975. .then((response) => {
  976. if ("search" == where) {
  977. this.queryExams4SearchLoading = false;
  978. this.examList4Search = response.data;
  979. }
  980. })
  981. .catch(() => {
  982. if ("search" == where) {
  983. this.queryExams4SearchLoading = false;
  984. }
  985. });
  986. },
  987. showStuExamDialog(row) {
  988. this.stuExamSearch.identityNumber = row.identityNumber;
  989. this.stuExamSearch.rootOrgId = row.rootOrgId;
  990. this.stuExamList = [];
  991. this.stuExamTotal = 0;
  992. this.queryExams4Search("");
  993. this.stuExamCurPage = 1;
  994. this.searchStuExam(this.stuExamCurPage);
  995. this.stuExamDialog = true;
  996. },
  997. searchExamRecord() {
  998. this.stuExamCurPage = 1;
  999. this.searchStuExam(this.stuExamCurPage);
  1000. },
  1001. searchStuExam(curPage) {
  1002. this.stuExamSearch.skipRule = true;
  1003. var param = new URLSearchParams(this.stuExamSearch);
  1004. this.stuExamLoading = true;
  1005. var url =
  1006. EXAM_WORK_API +
  1007. "/exam_student/examStudentPage/" +
  1008. (curPage - 1) +
  1009. "/" +
  1010. this.stuExamPageSize +
  1011. "?" +
  1012. param;
  1013. this.$httpWithMsg
  1014. .get(url)
  1015. .then((response) => {
  1016. this.stuExamList = response.data.list;
  1017. this.stuExamTotal = response.data.total;
  1018. this.stuExamLoading = false;
  1019. })
  1020. .catch(function (response) {
  1021. console.log(response);
  1022. this.stuExamLoading = false;
  1023. });
  1024. },
  1025. stuExamCurChange(val) {
  1026. this.stuExamCurPage = val;
  1027. this.searchStuExam(val);
  1028. },
  1029. handleStuExamSizeChange(val) {
  1030. this.stuExamPageSize = val;
  1031. this.searchStuExam(val);
  1032. },
  1033. closePhotoDialog() {
  1034. this.photo.url = "/img/blank.png";
  1035. this.photoDialog = false;
  1036. },
  1037. showPhoto(row) {
  1038. if (row.photoPath) {
  1039. this.photo.url = row.photoPath;
  1040. this.photoDialog = true;
  1041. } else {
  1042. this.$notify({
  1043. showClose: true,
  1044. message: "未上传照片",
  1045. type: "error",
  1046. });
  1047. }
  1048. },
  1049. uploadPhoto() {
  1050. this.photoUploadDialog = true;
  1051. this.initUpload();
  1052. },
  1053. selectChange(row) {
  1054. this.selectedStuIds = [];
  1055. row.forEach((element, index) => {
  1056. console.log(index);
  1057. this.selectedStuIds.push(element.id);
  1058. });
  1059. console.log(this.selectedStuIds);
  1060. },
  1061. handleCurrentChange(val) {
  1062. this.currentPage = val;
  1063. this.searchForm();
  1064. },
  1065. handleSizeChange(val) {
  1066. this.pageSize = val;
  1067. this.searchForm();
  1068. },
  1069. resetSearchForm() {
  1070. this.formSearch.name = "";
  1071. this.formSearch.studentCode = "";
  1072. this.formSearch.identityNumber = "";
  1073. this.formSearch.enable = "UNDEFINED";
  1074. this.formSearch.hasPhoto = "UNDEFINED";
  1075. if (!this.pureLC) {
  1076. this.formSearch.orgId = "";
  1077. }
  1078. },
  1079. resetPageAndSearchForm() {
  1080. this.currentPage = 1;
  1081. this.searchForm();
  1082. },
  1083. //查询方法
  1084. searchForm() {
  1085. var param = new URLSearchParams(this.formSearch);
  1086. var url =
  1087. CORE_API +
  1088. "/student/studentPage/" +
  1089. (this.currentPage - 1) +
  1090. "/" +
  1091. this.pageSize +
  1092. "?" +
  1093. param;
  1094. this.$httpWithMsg.get(url).then((response) => {
  1095. this.tableData = response.data.list;
  1096. this.total = response.data.total;
  1097. });
  1098. },
  1099. exportStudent() {
  1100. var param = new URLSearchParams(this.formSearch);
  1101. window.open(
  1102. CORE_API +
  1103. "/student/export?$key=" +
  1104. this.user.key +
  1105. "&$token=" +
  1106. this.user.token +
  1107. "&" +
  1108. param
  1109. );
  1110. },
  1111. checkIds(row) {
  1112. if (row) {
  1113. return row.id;
  1114. } else {
  1115. if (this.stuIds.length == 0) {
  1116. this.$notify({
  1117. type: "warning",
  1118. message: "请选择学生",
  1119. });
  1120. return "";
  1121. } else {
  1122. return this.stuIds;
  1123. }
  1124. }
  1125. },
  1126. showUnbindStudentCode(row) {
  1127. this.unbindStudentCodeData.tableData = [];
  1128. for (let cur of row.studentCodeList) {
  1129. this.unbindStudentCodeData.tableData.push({
  1130. studentCode: cur,
  1131. rootOrgId: row.rootOrgId,
  1132. name: row.name,
  1133. identityNumber: row.identityNumber,
  1134. });
  1135. }
  1136. this.unbindStudentCodeDialog = true;
  1137. },
  1138. unbindStudentCode(row) {
  1139. var params = new URLSearchParams({
  1140. studentCode: row.studentCode,
  1141. rootOrgId: row.rootOrgId,
  1142. });
  1143. this.$confirm("是否解绑学号?", "提示", {
  1144. confirmButtonText: "确定",
  1145. cancelButtonText: "取消",
  1146. type: "warning",
  1147. }).then(() => {
  1148. var url = CORE_API + "/student/unbindStudentCode?" + params;
  1149. this.$httpWithMsg.post(url).then((response) => {
  1150. console.log(response);
  1151. let newTableData = [];
  1152. for (let cur of this.unbindStudentCodeData.tableData) {
  1153. if (row.studentCode != cur.studentCode) {
  1154. newTableData.push(cur);
  1155. }
  1156. }
  1157. this.unbindStudentCodeData.tableData = newTableData;
  1158. this.$notify({
  1159. type: "success",
  1160. message: "解绑成功",
  1161. });
  1162. this.searchForm();
  1163. });
  1164. });
  1165. },
  1166. unbindSecurityPhone(row) {
  1167. var stuIds = this.checkIds(row);
  1168. if (!stuIds) return;
  1169. this.$confirm("是否解绑安全手机号?", "提示", {
  1170. confirmButtonText: "确定",
  1171. cancelButtonText: "取消",
  1172. type: "warning",
  1173. }).then(() => {
  1174. var url = CORE_API + "/student/unbindSecurityPhone/" + stuIds;
  1175. this.$httpWithMsg.put(url).then((response) => {
  1176. console.log(response);
  1177. this.$notify({
  1178. type: "success",
  1179. message: "解绑成功",
  1180. });
  1181. this.searchForm();
  1182. });
  1183. });
  1184. },
  1185. //重置密码方法
  1186. resetPassword(row) {
  1187. var stuIds = this.checkIds(row);
  1188. if (!stuIds) return;
  1189. this.$confirm("是否重置所选学生的密码?", "提示", {
  1190. confirmButtonText: "确定",
  1191. cancelButtonText: "取消",
  1192. type: "warning",
  1193. }).then(() => {
  1194. var url = CORE_API + "/student/resetPass/" + stuIds;
  1195. this.$httpWithMsg.put(url).then((response) => {
  1196. console.log(response);
  1197. this.$notify({
  1198. type: "success",
  1199. message: "重置密码成功",
  1200. });
  1201. this.searchForm();
  1202. });
  1203. });
  1204. },
  1205. //禁用
  1206. disableStudent(row) {
  1207. var stuIds = this.checkIds(row);
  1208. if (!stuIds) return;
  1209. this.$confirm("是否禁用所选学生?", "提示", {
  1210. confirmButtonText: "确定",
  1211. cancelButtonText: "取消",
  1212. type: "warning",
  1213. }).then(() => {
  1214. var url = CORE_API + "/student/disable/" + stuIds;
  1215. this.$httpWithMsg.put(url).then((response) => {
  1216. console.log(response);
  1217. this.$notify({
  1218. type: "success",
  1219. message: "禁用成功",
  1220. });
  1221. this.searchForm();
  1222. });
  1223. });
  1224. },
  1225. //启用
  1226. enableStudent(row) {
  1227. var stuIds = this.checkIds(row);
  1228. if (!stuIds) return;
  1229. this.$confirm("是否启用所选学生?", "提示", {
  1230. confirmButtonText: "确定",
  1231. cancelButtonText: "取消",
  1232. type: "warning",
  1233. }).then(() => {
  1234. var url = CORE_API + "/student/enable/" + stuIds;
  1235. this.$httpWithMsg.put(url).then((response) => {
  1236. console.log(response);
  1237. this.$notify({
  1238. type: "success",
  1239. message: "启用成功",
  1240. });
  1241. this.searchForm();
  1242. });
  1243. });
  1244. },
  1245. beforeUpload(file) {
  1246. console.log(file);
  1247. },
  1248. uploadProgress(event, file, fileList) {
  1249. console.log("uploadProgress");
  1250. console.log(event);
  1251. console.log(file);
  1252. console.log(fileList);
  1253. },
  1254. uploadSuccess(response, file, fileList) {
  1255. console.log(response);
  1256. console.log(file);
  1257. console.log(fileList);
  1258. this.$notify({
  1259. message: "上传成功",
  1260. type: "success",
  1261. });
  1262. this.fileLoading = false;
  1263. this.photoUploadDialog = false;
  1264. this.searchForm();
  1265. },
  1266. uploadError(response, file, fileList) {
  1267. console.log(response);
  1268. console.log(file);
  1269. console.log(fileList);
  1270. var json = JSON.parse(response.message);
  1271. if (response.status == 500) {
  1272. this.$notify({
  1273. message: json.desc,
  1274. type: "error",
  1275. });
  1276. }
  1277. this.fileLoading = false;
  1278. },
  1279. initUpload() {
  1280. this.fileList = [];
  1281. },
  1282. checkUpload() {
  1283. var fileList = this.$refs.upload.uploadFiles;
  1284. if (fileList.length == 0) {
  1285. this.$notify({
  1286. message: "上传文件不能为空",
  1287. type: "error",
  1288. });
  1289. return false;
  1290. }
  1291. if (fileList.length > 1) {
  1292. this.$notify({
  1293. message: "每次只能上传一个文件",
  1294. type: "error",
  1295. });
  1296. return false;
  1297. }
  1298. for (let file of fileList) {
  1299. let f = file.name.toLowerCase();
  1300. if (!f.endsWith(".jpg") && !f.endsWith(".png")) {
  1301. this.$notify({
  1302. message: "上传文件必须为jpg或者png格式",
  1303. type: "error",
  1304. });
  1305. this.initUpload();
  1306. return false;
  1307. }
  1308. }
  1309. return true;
  1310. },
  1311. //确定上传
  1312. submitUpload() {
  1313. if (!this.checkUpload()) {
  1314. return false;
  1315. }
  1316. this.$refs.upload.submit();
  1317. this.fileLoading = true;
  1318. },
  1319. //清空文件
  1320. removeFile() {
  1321. this.$refs.upload.clearFiles();
  1322. },
  1323. initPrivileges() {
  1324. let params = new URLSearchParams();
  1325. params.append(
  1326. "privilegeCodes",
  1327. Object.keys(this.rolePrivileges).toString()
  1328. );
  1329. let url = CORE_API + "/rolePrivilege/checkPrivileges?" + params;
  1330. this.$httpWithMsg.post(url).then((response) => {
  1331. this.rolePrivileges = response.data;
  1332. });
  1333. },
  1334. async init() {
  1335. this.initPrivileges();
  1336. for (let role of this.user.roleList) {
  1337. if (role.roleCode == "LC_USER") {
  1338. this.pureLC = true;
  1339. continue;
  1340. }
  1341. }
  1342. if (1 < this.user.roleList.length) {
  1343. this.pureLC = false;
  1344. }
  1345. this.formSearch.rootOrgId = this.user.rootOrgId;
  1346. this.getOrgList4Search("");
  1347. if (this.pureLC) {
  1348. let url = CORE_API + "/user/" + this.user.userId;
  1349. await this.$httpWithMsg.get(url).then((response) => {
  1350. var userInfo = response.data;
  1351. this.lc_id = userInfo.orgId;
  1352. // this.lc_name = userInfo.orgName;
  1353. // this.lc_code = userInfo.orgCode;
  1354. // this.orgList4Search = [
  1355. // { id: this.lc_id, name: this.lc_name, code: this.lc_code },
  1356. // ];
  1357. // this.formSearch.orgId = this.lc_id;
  1358. });
  1359. }
  1360. let url = CORE_API + "/org/getRootOrgList";
  1361. this.$httpWithMsg.get(url).then((response) => {
  1362. this.rootOrgList = response.data;
  1363. });
  1364. this.searchForm();
  1365. },
  1366. handleExamChange4Search(value) {
  1367. this.stuExamSearch.examStageId = "";
  1368. if (this.examList4Search.length > 0) {
  1369. let examArr = this.examList4Search.filter((p) => p.id == value);
  1370. if (examArr && examArr.length > 0) {
  1371. let exam = examArr[0];
  1372. if (
  1373. exam.specialSettingsEnabled &&
  1374. exam.specialSettingsType == "STAGE_BASED"
  1375. ) {
  1376. this.examStageDisabled4Search = false;
  1377. this.queryExamStages4Search("");
  1378. } else {
  1379. this.examStageList4Search = [];
  1380. this.examStageDisabled4Search = true;
  1381. }
  1382. }
  1383. }
  1384. },
  1385. queryExamStages4Search(name) {
  1386. this.queryExamStages(this.stuExamSearch.examId, name, "search");
  1387. },
  1388. queryExamStages(examId, name, where) {
  1389. console.log("queryExams; name: " + name);
  1390. this.$httpWithMsg
  1391. .get(
  1392. EXAM_WORK_API +
  1393. "/examStage/queryByNameLike?examId=" +
  1394. examId +
  1395. "&enable=true&name=" +
  1396. name
  1397. )
  1398. .then((response) => {
  1399. if ("search" == where) {
  1400. this.queryExamStages4SearchLoading = false;
  1401. this.examStageList4Search = response.data;
  1402. } else if ("insertOrUpdate" == where) {
  1403. this.queryExamStages4InsertOrUpdateLoading = false;
  1404. this.examStageList4InsertOrUpdate = response.data;
  1405. }
  1406. })
  1407. .catch((response) => {
  1408. console.log(response);
  1409. if ("search" == where) {
  1410. this.queryExamStages4SearchLoading = false;
  1411. } else if ("insertOrUpdate" == where) {
  1412. this.queryExamStages4InsertOrUpdateLoading = false;
  1413. }
  1414. });
  1415. },
  1416. },
  1417. };
  1418. </script>
  1419. <style scoped>
  1420. .input {
  1421. width: 180px;
  1422. }
  1423. </style>