index.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. import {
  2. DataTableColumn,
  3. NButton,
  4. NCascader,
  5. NDataTable,
  6. NForm,
  7. NFormItem,
  8. NIcon,
  9. NImage,
  10. NInput,
  11. NModal,
  12. NSpace,
  13. useDialog,
  14. useMessage
  15. } from 'naive-ui';
  16. import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue';
  17. import styles from './index.module.less';
  18. import { useUserStore } from '/src/store/modules/users';
  19. import UploadFile from '/src/components/upload-file';
  20. import { Add } from '@vicons/ionicons5';
  21. import {
  22. api_schoolUpdate,
  23. api_sysAreaQueryAllProvince,
  24. api_teacherPage,
  25. api_tenantInfoUpdateStatus,
  26. api_userResetPassword,
  27. updateAdmin
  28. } from '../../api';
  29. import AddTeacher from '../../modal/add-teacher';
  30. import TheQrCode from '/src/components/TheQrCode';
  31. import logo from '@/common/images/logo.png';
  32. import { stringifyQuery } from '/src/router';
  33. import AddteacherModel from '../../modal/addteacherModel';
  34. import TeacherGuide from '@/custom-plugins/guide-page/teacher-guide';
  35. import TheEmpty from '/src/components/TheEmpty';
  36. import TheMessageDialog from '/src/components/TheMessageDialog';
  37. export default defineComponent({
  38. name: 'school-info',
  39. emits: ['changeTab'],
  40. setup(props, { emit }) {
  41. const user = useUserStore();
  42. const formOptions = reactive({
  43. areaList: [] as any[]
  44. });
  45. const forms = reactive({
  46. name: user.info.schoolInfos?.[0]?.name,
  47. schoolId: user.info.schoolInfos?.[0]?.id,
  48. userId: user.info.id,
  49. logo: user.info.schoolInfos?.[0]?.logo || user.info.avatar,
  50. provinceCode: user.info.schoolInfos?.[0]?.provinceCode || '', // 省份编码
  51. cityCode: user.info.schoolInfos?.[0]?.cityCode || '', // 城市编码
  52. regionCode: user.info.schoolInfos?.[0]?.regionCode || '' // 区域编码
  53. });
  54. const data = reactive({
  55. loading: false,
  56. schoolLoading: true,
  57. dataList: [] as any[],
  58. disabled: true,
  59. changeVisiable: false,
  60. messageLoading: false,
  61. activeRow: {} as any,
  62. modal: false,
  63. qrModal: false,
  64. oldTecherform: {} as any,
  65. oldLoading: false
  66. });
  67. const showGuide = ref(false);
  68. const columns = (): DataTableColumn[] => {
  69. return [
  70. {
  71. title: '老师姓名',
  72. key: 'nickname',
  73. render: (row: any) => {
  74. return (
  75. <div
  76. style={{ userSelect: 'all', cursor: 'pointer' }}
  77. onClick={() => copyTo(row.nickname)}>
  78. {row.nickname}
  79. </div>
  80. );
  81. }
  82. },
  83. {
  84. title: '手机号码',
  85. key: 'phone',
  86. render: (row: any) => {
  87. return (
  88. <div
  89. style={{ userSelect: 'all', cursor: 'pointer' }}
  90. onClick={() => copyTo(row.phone)}>
  91. {row.phone}
  92. </div>
  93. );
  94. }
  95. },
  96. {
  97. title: '性别',
  98. key: 'questionTypeCode',
  99. render: (row: any) => {
  100. return <div>{row.gender ? '男' : '女'}</div>;
  101. }
  102. },
  103. {
  104. title: '状态',
  105. key: 'statusName',
  106. render: (row: any) => {
  107. return (
  108. <div>
  109. {row.status === 'ACTIVATION' ? (
  110. <NButton text>启用</NButton>
  111. ) : (
  112. <NButton class={styles.errorBtn} text>
  113. 冻结
  114. </NButton>
  115. )}
  116. </div>
  117. );
  118. }
  119. },
  120. {
  121. title: '操作',
  122. key: 'titleImg',
  123. render: (row: any) => (
  124. <NSpace>
  125. <NButton type="primary" text onClick={() => onResetPassword(row)}>
  126. 重置密码
  127. </NButton>
  128. {row.status === 'ACTIVATION' ? (
  129. <>
  130. <NButton
  131. disabled={row.jobType === 'ADMIN'}
  132. type="primary"
  133. text
  134. onClick={() => handleChange(row)}>
  135. 冻结
  136. </NButton>
  137. {row.jobType === 'TEACHER' && (
  138. <NButton
  139. type="primary"
  140. text
  141. onClick={() => {
  142. data.changeVisiable = true;
  143. data.activeRow = row;
  144. }}>
  145. 转交管理
  146. </NButton>
  147. )}
  148. </>
  149. ) : (
  150. <NButton
  151. class={styles.errorBtn}
  152. text
  153. onClick={() => handleChange(row)}>
  154. 解冻
  155. </NButton>
  156. )}
  157. </NSpace>
  158. )
  159. }
  160. ];
  161. };
  162. const getAreaList = async () => {
  163. const res = await api_sysAreaQueryAllProvince();
  164. if (res?.code === 200) {
  165. formOptions.areaList = res.data;
  166. }
  167. };
  168. const getList = async () => {
  169. data.loading = true;
  170. const res = await api_teacherPage({
  171. schoolId: user.info.schoolInfos?.[0]?.id,
  172. // jobType: 'TEACHER',
  173. // jobType: 'ADMIN',
  174. page: 1,
  175. rows: 1000
  176. });
  177. data.loading = false;
  178. if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
  179. data.dataList = res.data.rows;
  180. }
  181. setTimeout(() => {
  182. showGuide.value = true;
  183. }, 500);
  184. };
  185. const onChangeManage = async () => {
  186. console.log('111');
  187. data.messageLoading = true;
  188. try {
  189. await updateAdmin({
  190. school: forms.schoolId,
  191. newAdminId: data.activeRow.id,
  192. oldAdminId: forms.userId
  193. });
  194. message.success('转交成功');
  195. emit('changeTab', 'person');
  196. await user.getInfo();
  197. } catch {
  198. //
  199. }
  200. data.messageLoading = false;
  201. };
  202. onMounted(() => {
  203. getAreaList();
  204. getList();
  205. });
  206. const dialog = useDialog();
  207. const message = useMessage();
  208. const handleChange = (row: any) => {
  209. const statuStr = row.status === 'LOCKED' ? '解冻' : '冻结';
  210. dialog.warning({
  211. title: '温馨提示',
  212. content: `是否${statuStr}"${row.nickname}"?`,
  213. positiveText: '确定',
  214. negativeText: '取消',
  215. onPositiveClick: async () => {
  216. await api_tenantInfoUpdateStatus({
  217. ids: [row.id],
  218. status: row.status === 'LOCKED' ? 'ACTIVATION' : 'LOCKED'
  219. });
  220. getList();
  221. message.success(statuStr + '成功');
  222. }
  223. });
  224. };
  225. // 重置密码
  226. const onResetPassword = (row: any): void => {
  227. dialog.warning({
  228. title: '警告',
  229. content: `重置"${row.nickname}"的密码,是否继续?`,
  230. positiveText: '确定',
  231. negativeText: '取消',
  232. onPositiveClick: async () => {
  233. await api_userResetPassword({
  234. userId: row.id,
  235. clientType: 'TEACHER'
  236. });
  237. message.success('重置成功');
  238. }
  239. });
  240. };
  241. const formRef = ref();
  242. const changeSchoolInfo = () => {
  243. formRef.value?.validate(async (err: any) => {
  244. if (err) {
  245. return;
  246. }
  247. data.schoolLoading = false;
  248. await api_schoolUpdate({ ...user.info.schoolInfos?.[0], ...forms });
  249. data.schoolLoading = true;
  250. message.success('修改成功');
  251. data.disabled = true;
  252. });
  253. };
  254. const registerUrl = () => {
  255. const queryStr = `tenantId=${user.info.schoolInfos?.[0]?.tenantId}&schoolId=${user.info.schoolInfos?.[0]?.id}&schoolName=${user.info.schoolInfos?.[0]?.name}`;
  256. const url =
  257. `${location.origin}/classroom-app/#/teaher-register?` + queryStr;
  258. console.log(url);
  259. return url;
  260. };
  261. const copyTo = (text: string) => {
  262. const input = document.createElement('input');
  263. input.value = text;
  264. document.body.appendChild(input);
  265. input.select();
  266. input.setSelectionRange(0, input.value.length);
  267. document.execCommand('Copy');
  268. document.body.removeChild(input);
  269. message.success('复制成功');
  270. };
  271. return () => (
  272. <div class={styles.schoolInfo}>
  273. <NForm
  274. ref={formRef}
  275. class={styles.formWrap}
  276. model={forms}
  277. style={{ padding: '30px 0' }}
  278. disabled={data.disabled}>
  279. <NSpace size={[30, 20]}>
  280. <div class={styles.logo}>
  281. <NImage
  282. previewDisabled={false}
  283. src={forms.logo}
  284. objectFit="contain"
  285. />
  286. <div
  287. style={{ display: data.disabled ? 'none' : '' }}
  288. class={styles.changeHead}>
  289. 修改头像
  290. {data.schoolLoading && (
  291. <UploadFile
  292. class={[styles.uploadFile]}
  293. cropper
  294. onUpdate:fileList={val => {
  295. forms.logo = val;
  296. }}
  297. />
  298. )}
  299. </div>
  300. </div>
  301. <NFormItem
  302. label="学校名称"
  303. path="name"
  304. showRequireMark={false}
  305. rule={[
  306. { required: true, message: '请填写学校名称', trigger: 'blur' }
  307. ]}>
  308. <NInput
  309. bordered={!data.disabled}
  310. maxlength={20}
  311. v-model:value={forms.name}
  312. />
  313. </NFormItem>
  314. <NFormItem label="城区">
  315. {!data.oldLoading && (
  316. <NCascader
  317. placeholder="请选择城区"
  318. bordered={!data.disabled}
  319. options={formOptions.areaList}
  320. labelField="name"
  321. valueField="code"
  322. childrenField="areas"
  323. checkStrategy="child"
  324. expandTrigger="hover"
  325. defaultValue={
  326. user.info.schoolInfos?.[0]?.regionCode ||
  327. user.info.schoolInfos?.[0]?.cityCode ||
  328. user.info.schoolInfos?.[0]?.provinceCode
  329. }
  330. onUpdate:value={(val: any, option: any, pathValues: any) => {
  331. forms.provinceCode = pathValues[0]?.code;
  332. forms.cityCode = pathValues[1]?.code;
  333. forms.regionCode = pathValues[2]?.code;
  334. }}
  335. />
  336. )}
  337. </NFormItem>
  338. <NFormItem>
  339. {data.disabled ? (
  340. <NSpace class={styles.btnList} align="center" justify="end">
  341. <NButton
  342. class={styles.btn}
  343. color="#f24433"
  344. onClick={() => {
  345. data.oldTecherform = Object.assign({}, forms);
  346. data.disabled = false;
  347. }}>
  348. 修改信息
  349. </NButton>
  350. </NSpace>
  351. ) : (
  352. <NSpace class={styles.btnList} align="center" justify="end">
  353. <NButton
  354. class={styles.btn}
  355. onClick={() => {
  356. Object.assign(forms, data.oldTecherform);
  357. data.disabled = true;
  358. data.oldLoading = true;
  359. nextTick(() => {
  360. data.oldLoading = false;
  361. });
  362. }}>
  363. 取消
  364. </NButton>
  365. <NButton
  366. class={styles.btn}
  367. loading={!data.schoolLoading}
  368. type="primary"
  369. onClick={() => changeSchoolInfo()}>
  370. 完成
  371. </NButton>
  372. </NSpace>
  373. )}
  374. </NFormItem>
  375. </NSpace>
  376. </NForm>
  377. <NSpace style={{ padding: '0 0 32px' }}>
  378. <NButton
  379. focusable={false}
  380. {...{ id: 'teacher-0' }}
  381. type="primary"
  382. renderIcon={() => <NIcon component={<Add />} />}
  383. onClick={() => (data.modal = true)}>
  384. 添加老师
  385. </NButton>
  386. <NButton
  387. focusable={false}
  388. {...{ id: 'teacher-1' }}
  389. type="primary"
  390. onClick={() => (data.qrModal = true)}>
  391. 老师注册二维码
  392. </NButton>
  393. </NSpace>
  394. <NDataTable
  395. v-slots={{
  396. empty: () => <TheEmpty></TheEmpty>
  397. }}
  398. loading={data.loading}
  399. columns={columns()}
  400. data={data.dataList}></NDataTable>
  401. <NModal
  402. class={styles.addTeacher}
  403. v-model:show={data.modal}
  404. title="添加老师"
  405. preset="dialog"
  406. showIcon={false}>
  407. <AddTeacher
  408. areaList={formOptions.areaList}
  409. onClose={() => {
  410. data.modal = false;
  411. getList();
  412. }}
  413. />
  414. </NModal>
  415. {data.qrModal ? (
  416. <div v-model:show={data.qrModal} class="n-modal-mask">
  417. <AddteacherModel
  418. onClose={() => {
  419. data.qrModal = false;
  420. }}></AddteacherModel>
  421. </div>
  422. ) : null}
  423. {showGuide.value ? <TeacherGuide></TeacherGuide> : null}
  424. <NModal
  425. v-model:show={data.changeVisiable}
  426. preset="card"
  427. class={['modalTitle', styles.removeVisiable1]}
  428. title={'转交管理员'}>
  429. <TheMessageDialog
  430. content={`<p style="text-align: left;">转交管理员后,您当前账号将无法查看和更改学校信息,请确认是否转交给<span style="color: #198CFE">【${data.activeRow.nickname}】</span></p>`}
  431. cancelButtonText="取消"
  432. confirmButtonText="确认"
  433. loading={data.messageLoading}
  434. onClose={() => (data.changeVisiable = false)}
  435. onConfirm={onChangeManage}
  436. />
  437. </NModal>
  438. </div>
  439. );
  440. }
  441. });