index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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, 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. } from '../../api';
  28. import AddTeacher from '../../modal/add-teacher';
  29. import TheQrCode from '/src/components/TheQrCode';
  30. import { stringifyQuery } from '/src/router';
  31. export default defineComponent({
  32. name: 'school-info',
  33. setup() {
  34. const user = useUserStore();
  35. const formOptions = reactive({
  36. areaList: [] as any[]
  37. });
  38. const forms = reactive({
  39. name: user.info.schoolInfos?.[0]?.name,
  40. logo: user.info.schoolInfos?.[0]?.logo || user.info.avatar,
  41. provinceCode: user.info.schoolInfos?.[0]?.provinceCode || '', // 省份编码
  42. cityCode: user.info.schoolInfos?.[0]?.cityCode || '', // 城市编码
  43. regionCode: user.info.schoolInfos?.[0]?.regionCode || '' // 区域编码
  44. });
  45. const data = reactive({
  46. loading: false,
  47. schoolLoading: true,
  48. dataList: [] as any[],
  49. disabled: true,
  50. modal: false,
  51. qrModal: false
  52. });
  53. const columns = (): DataTableColumn[] => {
  54. return [
  55. {
  56. title: '老师姓名',
  57. key: 'nickname',
  58. render: (row: any) => {
  59. return (
  60. <div
  61. style={{ userSelect: 'all', cursor: 'pointer' }}
  62. onClick={() => copyTo(row.nickname)}>
  63. {row.nickname}
  64. </div>
  65. );
  66. }
  67. },
  68. {
  69. title: '手机号码',
  70. key: 'phone',
  71. render: (row: any) => {
  72. return (
  73. <div
  74. style={{ userSelect: 'all', cursor: 'pointer' }}
  75. onClick={() => copyTo(row.phone)}>
  76. {row.phone}
  77. </div>
  78. );
  79. }
  80. },
  81. {
  82. title: '性别',
  83. key: 'questionTypeCode',
  84. render: (row: any) => {
  85. return <div>{row.gender ? '男' : '女'}</div>;
  86. }
  87. },
  88. {
  89. title: '状态',
  90. key: 'statusName',
  91. render: (row: any) => {
  92. return (
  93. <div>
  94. {row.status === 'ACTIVATION' ? (
  95. <NButton text>{row.statusName}</NButton>
  96. ) : (
  97. <NButton class={styles.errorBtn} text>
  98. {row.statusName}
  99. </NButton>
  100. )}
  101. </div>
  102. );
  103. }
  104. },
  105. {
  106. title: '操作',
  107. key: 'titleImg',
  108. render: (row: any) => (
  109. <NSpace>
  110. <NButton
  111. type="primary"
  112. quaternary
  113. size="small"
  114. onClick={() => onResetPassword(row)}>
  115. 重置密码
  116. </NButton>
  117. {row.status === 'ACTIVATION' ? (
  118. <NButton
  119. type="primary"
  120. quaternary
  121. size="small"
  122. onClick={() => handleChange(row)}>
  123. 冻结
  124. </NButton>
  125. ) : (
  126. <NButton
  127. class={styles.errorBtn}
  128. quaternary
  129. size="small"
  130. onClick={() => handleChange(row)}>
  131. 解冻
  132. </NButton>
  133. )}
  134. </NSpace>
  135. )
  136. }
  137. ];
  138. };
  139. const getAreaList = async () => {
  140. const res = await api_sysAreaQueryAllProvince();
  141. if (res?.code === 200) {
  142. formOptions.areaList = res.data;
  143. }
  144. };
  145. const getList = async () => {
  146. data.loading = true;
  147. const res = await api_teacherPage({
  148. schoolId: user.info.schoolInfos?.[0]?.id,
  149. // jobType: 'TEACHER',
  150. // jobType: 'ADMIN',
  151. page: 1,
  152. rows: 1000
  153. });
  154. data.loading = false;
  155. if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
  156. data.dataList = res.data.rows;
  157. }
  158. };
  159. onMounted(() => {
  160. getAreaList();
  161. getList();
  162. });
  163. const dialog = useDialog();
  164. const message = useMessage();
  165. const handleChange = (row: any) => {
  166. const statuStr = row.status === 'LOCKED' ? '解冻' : '冻结';
  167. dialog.warning({
  168. title: '温馨提示',
  169. content: `是否${statuStr}"${row.nickname}"?`,
  170. positiveText: '确定',
  171. negativeText: '取消',
  172. onPositiveClick: async () => {
  173. await api_tenantInfoUpdateStatus({
  174. ids: [row.id],
  175. status: row.status === 'LOCKED' ? 'ACTIVATION' : 'LOCKED'
  176. });
  177. getList();
  178. message.success(statuStr + '成功');
  179. }
  180. });
  181. };
  182. // 重置密码
  183. const onResetPassword = (row: any): void => {
  184. dialog.warning({
  185. title: '警告',
  186. content: `重置"${row.nickname}"的密码,是否继续?`,
  187. positiveText: '确定',
  188. negativeText: '取消',
  189. onPositiveClick: async () => {
  190. await api_userResetPassword({
  191. userId: row.id,
  192. password: 'ktyq' + row.phone.substr(7),
  193. clientType: 'TEACHER'
  194. });
  195. message.success('重置成功');
  196. }
  197. });
  198. };
  199. const formRef = ref();
  200. const changeSchoolInfo = () => {
  201. formRef.value?.validate(async (err: any) => {
  202. if (err) {
  203. return;
  204. }
  205. data.schoolLoading = false;
  206. await api_schoolUpdate({ ...user.info.schoolInfos?.[0], ...forms });
  207. data.schoolLoading = true;
  208. message.success('修改成功');
  209. data.disabled = true;
  210. });
  211. };
  212. const registerUrl = () => {
  213. const queryStr = `tenantId=${user.info.schoolInfos?.[0]?.tenantId}&schoolId=${user.info.schoolInfos?.[0]?.id}&schoolName=${user.info.schoolInfos?.[0]?.name}`;
  214. const url =
  215. `${location.origin}/classroom-app/#/teaher-register?` + queryStr;
  216. console.log(url);
  217. return url;
  218. };
  219. const copyTo = (text: string) => {
  220. const input = document.createElement('input');
  221. input.value = text;
  222. document.body.appendChild(input);
  223. input.select();
  224. input.setSelectionRange(0, input.value.length);
  225. document.execCommand('Copy');
  226. document.body.removeChild(input);
  227. message.success('复制成功');
  228. };
  229. return () => (
  230. <div class={styles.schoolInfo}>
  231. <NSpace wrapItem={false} align="center">
  232. <div class={styles.logo}>
  233. <NImage
  234. previewDisabled={false}
  235. width={100}
  236. height={100}
  237. src={forms.logo}
  238. />
  239. <div
  240. style={{ display: data.disabled ? 'none' : '' }}
  241. class={styles.changeHead}>
  242. 修改头像
  243. {data.schoolLoading && (
  244. <UploadFile
  245. class={[styles.uploadFile]}
  246. cropper
  247. onUpdate:fileList={val => {
  248. forms.logo = val;
  249. }}
  250. />
  251. )}
  252. </div>
  253. </div>
  254. <NForm
  255. ref={formRef}
  256. class={styles.formWrap}
  257. model={forms}
  258. style={{ paddingTop: '30px', paddingLeft: '80px' }}
  259. disabled={data.disabled}>
  260. <NSpace size={100}>
  261. <NFormItem
  262. label="学校名称"
  263. path="name"
  264. showRequireMark={false}
  265. rule={[
  266. { required: true, message: '请填写学校名称', trigger: 'blur' }
  267. ]}>
  268. <NInput
  269. bordered={!data.disabled}
  270. maxlength={20}
  271. v-model:value={forms.name}
  272. />
  273. </NFormItem>
  274. <NFormItem label="城区">
  275. <NCascader
  276. placeholder="请选择城区"
  277. bordered={!data.disabled}
  278. options={formOptions.areaList}
  279. labelField="name"
  280. valueField="code"
  281. childrenField="areas"
  282. checkStrategy="child"
  283. expandTrigger="hover"
  284. defaultValue={
  285. user.info.schoolInfos?.[0]?.regionCode ||
  286. user.info.schoolInfos?.[0]?.cityCode
  287. }
  288. onUpdate:value={(val: any, option: any, pathValues: any) => {
  289. forms.provinceCode = pathValues[0]?.code;
  290. forms.cityCode = pathValues[1]?.code;
  291. forms.regionCode = pathValues[2]?.code;
  292. }}
  293. />
  294. </NFormItem>
  295. <NFormItem>
  296. {data.disabled ? (
  297. <NSpace class={styles.btnList} align="center" justify="end">
  298. <NButton
  299. class={styles.btn}
  300. color="#f24433"
  301. onClick={() => (data.disabled = false)}>
  302. 修改信息
  303. </NButton>
  304. </NSpace>
  305. ) : (
  306. <NSpace class={styles.btnList} align="center" justify="end">
  307. <NButton
  308. class={styles.btn}
  309. onClick={() => (data.disabled = true)}>
  310. 取消
  311. </NButton>
  312. <NButton
  313. class={styles.btn}
  314. loading={!data.schoolLoading}
  315. type="primary"
  316. onClick={() => changeSchoolInfo()}>
  317. 完成
  318. </NButton>
  319. </NSpace>
  320. )}
  321. </NFormItem>
  322. </NSpace>
  323. </NForm>
  324. </NSpace>
  325. <NSpace style={{ padding: '32px 0' }}>
  326. <NButton
  327. type="primary"
  328. renderIcon={() => <NIcon component={<Add />} />}
  329. onClick={() => (data.modal = true)}>
  330. 添加老师
  331. </NButton>
  332. <NButton type="primary" onClick={() => (data.qrModal = true)}>
  333. 老师注册二维码
  334. </NButton>
  335. </NSpace>
  336. <NDataTable
  337. loading={data.loading}
  338. columns={columns()}
  339. data={data.dataList}></NDataTable>
  340. <NModal
  341. class={styles.addTeacher}
  342. v-model:show={data.modal}
  343. title="添加老师"
  344. preset="dialog"
  345. showIcon={false}>
  346. <AddTeacher
  347. areaList={formOptions.areaList}
  348. onClose={() => {
  349. data.modal = false;
  350. getList();
  351. }}
  352. />
  353. </NModal>
  354. <NModal
  355. v-model:show={data.qrModal}
  356. title="二维码"
  357. preset="dialog"
  358. showIcon={false}>
  359. <div style={{ textAlign: 'center' }}>
  360. <TheQrCode text={registerUrl()} size={300} />
  361. </div>
  362. </NModal>
  363. </div>
  364. );
  365. }
  366. });