classStudent.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. import { defineComponent, onMounted, reactive, watch } from 'vue';
  2. import styles from '../index.module.less';
  3. import {
  4. NButton,
  5. NDataTable,
  6. NForm,
  7. NFormItem,
  8. NModal,
  9. NNumberAnimation,
  10. NSpace,
  11. NTooltip,
  12. useMessage
  13. } from 'naive-ui';
  14. import SearchInput from '@/components/searchInput';
  15. import CSelect from '@/components/CSelect';
  16. import Pagination from '@/components/pagination';
  17. import { api_studentStat, getStudentList } from '../api';
  18. import { useRoute, useRouter } from 'vue-router';
  19. import TheEmpty from '/src/components/TheEmpty';
  20. import UpdateStudent from '../../studentList/modals/update-student';
  21. import { initCache, setCache } from '/src/hooks/use-async';
  22. import { modalClickMask } from '/src/state';
  23. export default defineComponent({
  24. name: 'student-studentList',
  25. props: {
  26. upgradeFlag: {
  27. type: Number
  28. }
  29. },
  30. setup(props) {
  31. const message = useMessage();
  32. const route = useRoute();
  33. const router = useRouter();
  34. const payForm = reactive({
  35. vipStudentNum: 0,
  36. studentNum: 0
  37. });
  38. const state = reactive({
  39. upgradeFlag: props.upgradeFlag == 0 ? true : false, // 是否为历史班
  40. searchForm: { keyword: '', gender: '' as any, membership: '' as any },
  41. loading: false,
  42. pagination: {
  43. page: 1,
  44. rows: 10,
  45. pageTotal: 4
  46. },
  47. tableList: [] as any,
  48. editStatus: false,
  49. activeRow: {} as any
  50. });
  51. watch(
  52. () => props.upgradeFlag,
  53. () => {
  54. state.upgradeFlag = props.upgradeFlag == 0 ? true : false;
  55. }
  56. );
  57. const search = () => {
  58. state.pagination.page = 1;
  59. getList();
  60. setCache({ current: state.searchForm, saveKey: 'classDetailStudent' });
  61. };
  62. const onReset = () => {
  63. state.searchForm = {
  64. keyword: '',
  65. gender: '' as any,
  66. membership: '' as any
  67. };
  68. search();
  69. setCache({ current: state.searchForm, saveKey: 'classDetailStudent' });
  70. };
  71. const getList = async () => {
  72. state.loading = true;
  73. try {
  74. const res = await getStudentList({
  75. classGroupId: route.query.id,
  76. ...state.searchForm,
  77. ...state.pagination
  78. });
  79. state.tableList = res.data.rows;
  80. state.pagination.pageTotal = res.data.total;
  81. state.loading = false;
  82. } catch (e) {
  83. state.loading = false;
  84. console.log(e);
  85. }
  86. };
  87. initCache({
  88. current: state.searchForm,
  89. saveKey: 'classDetailStudent',
  90. callBack: (active: any) => {
  91. state.searchForm = active;
  92. }
  93. });
  94. const getInfo = async () => {
  95. try {
  96. const { data } = await api_studentStat({
  97. classGroupId: route.query.id as any
  98. // ...state.searchForm
  99. });
  100. payForm.studentNum = data.studentNum || 0;
  101. payForm.vipStudentNum = data.vipStudentNum || 0;
  102. } catch (e) {
  103. console.log(e);
  104. }
  105. };
  106. onMounted(() => {
  107. getInfo();
  108. getList();
  109. });
  110. const copyTo = (text: string) => {
  111. const input = document.createElement('input');
  112. input.value = text;
  113. document.body.appendChild(input);
  114. input.select();
  115. input.setSelectionRange(0, input.value.length);
  116. document.execCommand('Copy');
  117. document.body.removeChild(input);
  118. message.success('复制成功');
  119. };
  120. const gotoDetail = (row: any) => {
  121. router.push({
  122. path: '/classStudentDetail',
  123. query: {
  124. ...route.query,
  125. studentId: row.id,
  126. studentName: row.nickname,
  127. upgradeFlag: state.upgradeFlag ? 0 : 1
  128. }
  129. });
  130. };
  131. const columns = () => {
  132. return [
  133. {
  134. title: '学生姓名',
  135. key: 'nickname',
  136. render: (row: any) => {
  137. return (
  138. <NTooltip showArrow={false} placement="top-start">
  139. {{
  140. trigger: () => (
  141. <div
  142. style={{ userSelect: 'all', cursor: 'pointer' }}
  143. onClick={() => copyTo(row.nickname)}>
  144. {row.nickname}
  145. </div>
  146. ),
  147. default: '点击复制'
  148. }}
  149. </NTooltip>
  150. );
  151. }
  152. },
  153. {
  154. title: '手机号',
  155. key: 'phone',
  156. render: (row: any) => {
  157. return (
  158. <NTooltip showArrow={false} placement="top-start">
  159. {{
  160. trigger: () => (
  161. <div
  162. style={{ userSelect: 'all', cursor: 'pointer' }}
  163. onClick={() => copyTo(row.phone)}>
  164. {row.phone}
  165. </div>
  166. ),
  167. default: '点击复制'
  168. }}
  169. </NTooltip>
  170. );
  171. }
  172. },
  173. {
  174. title: '性别',
  175. key: 'gender',
  176. render(row: any) {
  177. return (
  178. <>
  179. {row.gender + '' != 'null'
  180. ? row.gender == '0'
  181. ? '女'
  182. : '男'
  183. : '--'}
  184. </>
  185. );
  186. }
  187. },
  188. {
  189. title: '学生类型',
  190. key: 'vipMember',
  191. render(row: any) {
  192. return <>{row.vipMember ? '会员' : '普通'}</>;
  193. }
  194. },
  195. {
  196. title: '操作',
  197. key: 'id',
  198. render(row: any) {
  199. return (
  200. <NSpace>
  201. <NButton text type="primary" onClick={() => gotoDetail(row)}>
  202. 详情
  203. </NButton>
  204. <NButton
  205. text
  206. type="primary"
  207. onClick={() => onUpdate(row)}
  208. disabled={row.historyClassStudent}>
  209. 修改
  210. </NButton>
  211. </NSpace>
  212. );
  213. }
  214. }
  215. ];
  216. };
  217. // 修改
  218. const onUpdate = (row: any) => {
  219. state.editStatus = true;
  220. state.activeRow = row;
  221. };
  222. return () => (
  223. <div>
  224. <div class={['section-container']}>
  225. <div class={styles.TrainDataTop}>
  226. <div class={styles.TrainDataTopLeft}>
  227. <div class={styles.TrainDataItem}>
  228. <p class={styles.TrainDataItemTitle}>
  229. <div>
  230. <span>
  231. <NNumberAnimation
  232. from={0}
  233. to={payForm.studentNum}></NNumberAnimation>
  234. </span>
  235. </div>
  236. </p>
  237. <p class={styles.TrainDataItemsubTitle}>班级人数</p>
  238. </div>
  239. <div class={styles.TrainDataItem}>
  240. <p class={styles.TrainDataItemTitle}>
  241. <div>
  242. <span>
  243. <NNumberAnimation
  244. from={0}
  245. to={payForm.vipStudentNum}></NNumberAnimation>
  246. </span>
  247. </div>
  248. </p>
  249. <p class={styles.TrainDataItemsubTitle}>会员人数</p>
  250. </div>
  251. </div>
  252. <div class={styles.TrainDataTopRight}></div>
  253. </div>
  254. </div>
  255. <div class={styles.searchList}>
  256. <NForm label-placement="left" inline>
  257. <NFormItem>
  258. <SearchInput
  259. {...{ placeholder: '请输入学生姓名' }}
  260. class={styles.searchInput}
  261. searchWord={state.searchForm.keyword}
  262. onChangeValue={(val: string) =>
  263. (state.searchForm.keyword = val)
  264. }></SearchInput>
  265. </NFormItem>
  266. <NFormItem>
  267. <CSelect
  268. {...({
  269. options: [
  270. {
  271. label: '全部性别',
  272. value: ''
  273. },
  274. {
  275. label: '男',
  276. value: '1'
  277. },
  278. {
  279. label: '女',
  280. value: '0'
  281. }
  282. ],
  283. placeholder: '性别',
  284. clearable: true,
  285. inline: true
  286. } as any)}
  287. v-model:value={state.searchForm.gender}></CSelect>
  288. </NFormItem>
  289. <NFormItem>
  290. <CSelect
  291. {...({
  292. options: [
  293. {
  294. label: '全部类型',
  295. value: ''
  296. },
  297. {
  298. label: '会员',
  299. value: true
  300. },
  301. {
  302. label: '普通',
  303. value: false
  304. }
  305. ],
  306. placeholder: '学生类型',
  307. clearable: true,
  308. inline: true
  309. } as any)}
  310. v-model:value={state.searchForm.membership}></CSelect>
  311. </NFormItem>
  312. <NFormItem>
  313. <NSpace justify="end">
  314. <NButton type="primary" class="searchBtn" onClick={search}>
  315. 搜索
  316. </NButton>
  317. <NButton
  318. type="primary"
  319. ghost
  320. class="resetBtn"
  321. onClick={onReset}>
  322. 重置
  323. </NButton>
  324. </NSpace>
  325. </NFormItem>
  326. </NForm>
  327. </div>
  328. <div class={styles.tableWrap}>
  329. <NDataTable
  330. v-slots={{
  331. empty: () => <TheEmpty></TheEmpty>
  332. }}
  333. class={styles.classTable}
  334. loading={state.loading}
  335. columns={columns()}
  336. data={state.tableList}></NDataTable>
  337. <Pagination
  338. v-model:page={state.pagination.page}
  339. v-model:pageSize={state.pagination.rows}
  340. v-model:pageTotal={state.pagination.pageTotal}
  341. onList={getList}
  342. sync
  343. />
  344. </div>
  345. <NModal
  346. maskClosable={modalClickMask}
  347. v-model:show={state.editStatus}
  348. class={['modalTitle background', styles.updateStudent]}
  349. preset="card"
  350. title="修改信息">
  351. <UpdateStudent
  352. onClose={() => (state.editStatus = false)}
  353. onConfirm={() => getList()}
  354. row={state.activeRow}
  355. />
  356. </NModal>
  357. </div>
  358. );
  359. }
  360. });