tenantDataSchool.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import { defineComponent, onMounted, reactive, ref } from 'vue';
  2. import styles from './index.module.less';
  3. import {
  4. Cell,
  5. CellGroup,
  6. Col,
  7. List,
  8. Picker,
  9. Popup,
  10. Row,
  11. Tab,
  12. Tabs,
  13. DatePicker,
  14. Button
  15. } from 'vant';
  16. import MSticky from '@/components/m-sticky';
  17. import personIcon from './images/personIcon.png';
  18. import homeIcon from './images/icon-class.png';
  19. import memberIcon from './images/memberIcon.png';
  20. import memberRateIcon from './images/memberRateIcon.png';
  21. import sanIcon from './images/san.png';
  22. import request from '@/helpers/request';
  23. import topDot from './images/topDot.png';
  24. import { useRoute, useRouter } from 'vue-router';
  25. import { moneyFormat, numberFormat } from '@/helpers/utils';
  26. import OFullRefresh from '@/components/m-full-refresh';
  27. import OEmpty from '@/components/m-empty';
  28. import arrowIcon from './images/arrowIcon.png';
  29. import OHeader from '@/components/m-header';
  30. import OSearch from '@/components/m-search';
  31. import numeral from 'numeral';
  32. import MQrcode from '@/components/m-qrcode';
  33. import html2canvas from 'html2canvas';
  34. import MWxTip from '@/components/m-wx-tip';
  35. export default defineComponent({
  36. name: 'tenant-apply-data',
  37. setup() {
  38. const route = useRoute();
  39. const router = useRouter();
  40. const tabName = ref('all');
  41. const forms = reactive({
  42. keyword: '',
  43. id: route.query.id,
  44. // id: '1687275949971763202',
  45. yearStatus: false,
  46. schoolId: null,
  47. year: [new Date().getFullYear()] as any,
  48. yearName: new Date().getFullYear(),
  49. classList: [] as any,
  50. statObj: {
  51. registerNum: 0,
  52. classNum: 0,
  53. registerMemberShipNum: 0
  54. } as any,
  55. perponStatus: false,
  56. sortKey: 'CLASS' as 'CLASS' | 'MEMBER',
  57. sortId: 'desc',
  58. sortName: '报名人数降序',
  59. sortList: [
  60. { value: 'desc', text: '报名人数降序' },
  61. { value: 'asc', text: '报名人数升序' },
  62. { value: 'mdesc', text: '会员人数降序' },
  63. { value: 'masc', text: '会员人数升序' }
  64. ] as any,
  65. page: 1,
  66. rows: 20,
  67. isClick: false
  68. });
  69. const refreshing = ref(false);
  70. const loading = ref(true);
  71. const finished = ref(false);
  72. const showContact = ref(false);
  73. const list = ref([]);
  74. // const getSchoolList = async () => {
  75. // try {
  76. // const { data } = await request.get('/edu-app/open/school/list', {
  77. // params: {
  78. // tenantId: forms.id
  79. // }
  80. // });
  81. // const temp = [
  82. // {
  83. // value: '',
  84. // text: '全部学校'
  85. // }
  86. // ];
  87. // if (Array.isArray(data)) {
  88. // data.forEach((item: any) => {
  89. // temp.push({
  90. // value: item.id,
  91. // text: item.name
  92. // });
  93. // });
  94. // forms.classList = temp;
  95. // }
  96. // } catch {
  97. // //
  98. // }
  99. // };
  100. const getList = async () => {
  101. if (forms.isClick) {
  102. return;
  103. }
  104. forms.isClick = true;
  105. if (refreshing.value) {
  106. list.value = [];
  107. forms.page = 1;
  108. refreshing.value = false;
  109. }
  110. try {
  111. const res = await request.post(
  112. '/edu-app/open/school/schoolRegisterClassPage',
  113. {
  114. data: {
  115. schoolId: forms.id,
  116. page: forms.page,
  117. rows: forms.rows,
  118. // year: forms.yearName,
  119. keyword: forms.keyword,
  120. sort: forms.sortId,
  121. sortKey: forms.sortKey
  122. }
  123. }
  124. );
  125. if (list.value.length > 0 && res.data.current === 1) {
  126. return;
  127. }
  128. list.value = list.value.concat(res.data.rows || []);
  129. forms.page = res.data.current + 1;
  130. showContact.value = list.value.length > 0;
  131. loading.value = false;
  132. finished.value = res.data.current >= res.data.pages;
  133. } catch {
  134. showContact.value = false;
  135. finished.value = true;
  136. } finally {
  137. loading.value = false;
  138. }
  139. forms.isClick = false;
  140. };
  141. const onRefresh = () => {
  142. finished.value = false;
  143. // 重新加载数据
  144. // 将 loading 设置为 true,表示处于加载状态
  145. loading.value = true;
  146. getList();
  147. };
  148. const imgs = reactive({
  149. saveLoading: false,
  150. image: null as any,
  151. shareLoading: false
  152. });
  153. // 计算会员占比
  154. const formatMemberRate = (studentNum: any, memberNum: any) => {
  155. if (studentNum <= 0 || memberNum <= 0) {
  156. return 0;
  157. }
  158. // console.log(studentNum, memberNum);
  159. return Math.round((memberNum / studentNum) * 1000) / 10;
  160. };
  161. onMounted(async () => {
  162. if (route.query.name) {
  163. document.title = route.query.name + '报名统计';
  164. } else {
  165. document.title = '学生报名统计';
  166. }
  167. await getStat();
  168. await getList();
  169. });
  170. const getStat = async () => {
  171. try {
  172. const { data } = await request.post(
  173. '/edu-app/open/school/schoolRegisterStat',
  174. {
  175. data: {
  176. schoolId: forms.id
  177. // year: forms.yearName
  178. }
  179. }
  180. );
  181. forms.statObj = data;
  182. } catch {
  183. //
  184. }
  185. };
  186. return () => (
  187. <div class={styles.tenantAllData}>
  188. {/* <OHeader isBack={false} /> */}
  189. <MSticky position="top">
  190. <div class={styles.top}>
  191. <div class={styles.topWrap}>
  192. <div class={styles.topHead}>
  193. <img src={topDot} class={styles.topDot} alt="" />
  194. 汇总数据
  195. </div>
  196. {/* <div
  197. class={styles.timerWrap}
  198. onClick={() => {
  199. forms.yearStatus = true;
  200. }}>
  201. {forms.yearName}年{' '}
  202. <img
  203. src={sanIcon}
  204. class={[
  205. styles.sanIcon,
  206. forms.yearStatus ? styles.routeSan : ''
  207. ]}
  208. alt=""
  209. />
  210. </div> */}
  211. </div>
  212. <div class={styles.cardWrap}>
  213. <div class={[styles.classCard, styles.cardItem]}>
  214. <div class={styles.cardNum}>
  215. {numeral(forms.statObj.classNum).format('0,0')}
  216. </div>
  217. <div class={styles.cardInfo}>
  218. <img src={homeIcon} class={styles.cardInfoImg} alt="" />
  219. 班级数量
  220. </div>
  221. <div class={styles.cardLine}></div>
  222. </div>
  223. <div class={[styles.memberRateCard, styles.cardItem]}>
  224. <div class={styles.cardNum}>
  225. {numeral(
  226. formatMemberRate(
  227. forms.statObj.registerNum || 0,
  228. forms.statObj.registerMemberShipNum || 0
  229. )
  230. ).format('0.0')}
  231. %
  232. </div>
  233. <div class={styles.cardInfo}>
  234. <img src={memberRateIcon} class={styles.cardInfoImg} alt="" />
  235. 会员人数占比
  236. </div>
  237. <div class={styles.cardLine}></div>
  238. </div>
  239. <div class={[styles.studentCard, styles.cardItem]}>
  240. <div class={styles.cardNum}>
  241. {numeral(forms.statObj.registerNum).format('0,0')}
  242. </div>
  243. <div class={styles.cardInfo}>
  244. <img src={personIcon} class={styles.cardInfoImg} alt="" />
  245. 报名人数
  246. </div>
  247. <div class={styles.cardLine}></div>
  248. </div>
  249. <div class={[styles.memberCard, styles.cardItem]}>
  250. <div class={styles.cardNum}>
  251. {numeral(forms.statObj.registerMemberShipNum).format('0,0')}
  252. </div>
  253. <div class={styles.cardInfo}>
  254. <img src={memberIcon} class={styles.cardInfoImg} alt="" />
  255. 会员人数
  256. </div>
  257. <div class={styles.cardLine}></div>
  258. </div>
  259. </div>
  260. <div class={styles.searchWrap}>
  261. <div class={styles.searechInfo}>
  262. <OSearch
  263. class={styles.allDataWrap}
  264. shape="round"
  265. background="#F6F8F9"
  266. inputBackground="white"
  267. placeholder="请输入班级名称"
  268. onSearch={val => {
  269. forms.keyword = val;
  270. forms.page = 1;
  271. refreshing.value = true;
  272. getList();
  273. }}></OSearch>
  274. </div>
  275. <div
  276. class={styles.timerWrap}
  277. onClick={() => (forms.perponStatus = true)}>
  278. {forms.sortName}{' '}
  279. <img
  280. src={sanIcon}
  281. class={[
  282. styles.sanIcon,
  283. forms.perponStatus ? styles.routeSan : ''
  284. ]}
  285. alt=""
  286. />
  287. </div>
  288. </div>
  289. </div>
  290. </MSticky>
  291. <div class={styles.schoolList}>
  292. {showContact.value ? (
  293. <OFullRefresh
  294. v-model:modelValue={refreshing.value}
  295. onRefresh={onRefresh}
  296. class={styles.refreshC}>
  297. <List
  298. loading-text=" "
  299. finished={finished.value}
  300. finished-text=" "
  301. onLoad={getList}>
  302. {list.value.map((item: any) => (
  303. <div class={styles.schoolItem}>
  304. <div class={styles.schoolNameWrap}>
  305. {/* <p class={styles.title}>学校名称</p> */}
  306. <p class={styles.schoolName}>{item.classGroupName}</p>
  307. </div>
  308. <div class={styles.schoolCountWrap}>
  309. <div>
  310. <p class={styles.personNum}>
  311. {numeral(item.registerNum || 0).format('0,0')}
  312. {/* {item.registerNum || 0} */}
  313. </p>
  314. <p class={styles.title}>报名人数</p>
  315. </div>
  316. <div>
  317. <p class={styles.personNum}>
  318. {numeral(item.registerMemberShipNum || 0).format(
  319. '0,0'
  320. )}
  321. {/* {item.registerMemberShipNum || 0} */}
  322. </p>
  323. <p class={styles.title}>会员人数</p>
  324. </div>
  325. <div>
  326. <p class={styles.personNum}>
  327. {numeral(
  328. formatMemberRate(
  329. item.registerNum || 0,
  330. item.registerMemberShipNum || 0
  331. )
  332. ).format('0.0')}
  333. %
  334. </p>
  335. <p class={styles.title}>会员人数占比</p>
  336. </div>
  337. {/* <img class={[styles.arrow]} src={arrowIcon} alt="" /> */}
  338. </div>
  339. </div>
  340. ))}
  341. </List>
  342. </OFullRefresh>
  343. ) : null}
  344. {!showContact.value && !loading.value && (
  345. <OEmpty description="暂无学校信息" class={styles.emptyC} />
  346. )}
  347. </div>
  348. <Popup
  349. v-model:show={forms.yearStatus}
  350. position="bottom"
  351. round
  352. class={'popupBottomSearch'}>
  353. <DatePicker
  354. showToolbar
  355. v-model={forms.year}
  356. columns-type={['year']}
  357. onCancel={() => (forms.yearStatus = false)}
  358. onConfirm={(val: any) => {
  359. console.log(val);
  360. // const selectedOption = val.selectedOptions[0];
  361. // console.log(selectedOption, 'selectedOption');
  362. // forms.schoolId = selectedOption.value;
  363. // // forms.schoolName = selectedOption.text;
  364. forms.year = [val.selectedValues[0]];
  365. forms.yearName = val.selectedValues[0];
  366. forms.page = 1;
  367. refreshing.value = true;
  368. getStat();
  369. getList();
  370. forms.yearStatus = false;
  371. }}
  372. />
  373. </Popup>
  374. <Popup
  375. v-model:show={forms.perponStatus}
  376. position="bottom"
  377. round
  378. class={'popupBottomSearch'}>
  379. <Picker
  380. showToolbar
  381. columns={forms.sortList}
  382. onCancel={() => (forms.perponStatus = false)}
  383. onConfirm={(val: any) => {
  384. const selectedOption = val.selectedOptions[0];
  385. if (
  386. selectedOption.value === 'desc' ||
  387. selectedOption.value === 'asc'
  388. ) {
  389. forms.sortId = selectedOption.value;
  390. forms.sortKey = 'CLASS';
  391. }
  392. if (selectedOption.value === 'mdesc') {
  393. forms.sortId = 'desc';
  394. forms.sortKey = 'MEMBER';
  395. }
  396. if (selectedOption.value === 'masc') {
  397. forms.sortId = 'asc';
  398. forms.sortKey = 'MEMBER';
  399. }
  400. forms.sortName = selectedOption.text;
  401. refreshing.value = true;
  402. getList();
  403. forms.perponStatus = false;
  404. }}
  405. />
  406. </Popup>
  407. <MWxTip />
  408. </div>
  409. );
  410. }
  411. });