index.tsx 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /*
  2. * @Author: code4eat awesomedema@gmail.com
  3. * @Date: 2023-03-03 11:30:33
  4. * @LastEditors: code4eat awesomedema@gmail.com
  5. * @LastEditTime: 2024-12-31 13:58:04
  6. * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. */
  9. import KCIMPagecontainer from '@/components/KCIMPageContainer';
  10. import { KCIMTable } from '@/components/KCIMTable';
  11. import { createFromIconfontCN, InboxOutlined } from '@ant-design/icons';
  12. import { ActionType, arrayMoveImmutable, ProFormText, ProFormTextArea, useRefFunction } from '@ant-design/pro-components';
  13. import ProForm, { ModalForm, ProFormCascader, ProFormCheckbox, ProFormDatePicker, ProFormDateRangePicker, ProFormDependency, ProFormDigit, ProFormInstance, ProFormRadio, ProFormSelect, ProFormSwitch, ProFormUploadDragger } from '@ant-design/pro-form'
  14. import { ProColumns } from '@ant-design/pro-table';
  15. import { Alert, Drawer, Dropdown, Form, Input, message, Popconfirm, Popover, Radio } from 'antd';
  16. import { Fragment, Key, useEffect, useRef, useState } from 'react';
  17. import { addData, delData, editData, getMyInfoReq, getTableDataReq } from './service';
  18. import './style.less';
  19. import { getDicDataBySysId } from '@/services/getDic';
  20. import { authTimeType, KcimCenterSysId } from '@/constant';
  21. import 'moment/locale/zh-cn';
  22. import moment from 'moment';
  23. import { sparse } from 'mathjs';
  24. import AuthHisContent from './authHisttory';
  25. import FormTabs from './tabs';
  26. import { calculateDelayedDate } from '@/utils/tooljs';
  27. import { debounce } from 'lodash';
  28. import { useModel } from '@umijs/max';
  29. const IconFont = createFromIconfontCN({
  30. scriptUrl: '',
  31. });
  32. export default function MyQualifications() {
  33. const { initialState, setInitialState } = useModel('@@initialState');
  34. const formRef = useRef<ProFormInstance>();
  35. const drawerFormRef = useRef<ProFormInstance>();
  36. const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
  37. const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
  38. const [drawerVisible, set_drawerVisible] = useState(false);
  39. const [currentEditRow, set_currentEditRow] = useState<any>(undefined);
  40. const [currentSelectedTableRows, set_currentSelectedTableRows] = useState<any[]>([]);
  41. const tableRef = useRef<ActionType>();
  42. const [modalFormVisible, set_modalFormVisible] = useState(false);
  43. const [operationLevel, set_operationLevel] = useState<any[]>([]);
  44. const [userInfo, set_userInfo] = useState<any>(undefined);
  45. const [currentEditAttachment, set_currentEditAttachment] = useState<any>(undefined);
  46. const columns: ProColumns[] = [
  47. {
  48. title: '资质编码',
  49. ellipsis: true,
  50. renderText(text, record, index, action) {
  51. const { qualification: { code } } = record;
  52. return code
  53. },
  54. },
  55. {
  56. title: '资质名称',
  57. ellipsis: true,
  58. renderText(text, record, index, action) {
  59. const { qualification: { name } } = record;
  60. return name
  61. },
  62. },
  63. {
  64. title: '资质分类',
  65. ellipsis: true,
  66. renderText(text, record, index, action) {
  67. const { qualification: { qualificationTypeName } } = record;
  68. return qualificationTypeName
  69. },
  70. },
  71. {
  72. title: '特殊类别',
  73. ellipsis: true,
  74. width: 150,
  75. renderText(text, record, index, action) {
  76. const { qualification: { operationFlag, techFlag, operationLevelCode } } = record;
  77. return <div>
  78. {
  79. techFlag == 1 && (
  80. <span style={{
  81. display: 'inline-flex', width: 64, height: 20, justifyContent: 'center',
  82. alignItems: 'center', background: '#FFF5EB', borderRadius: 4, fontSize: 12, color: '#FF8000', marginRight: 4
  83. }}>医疗技术</span>
  84. )
  85. }
  86. {
  87. operationFlag == 1 && operationLevelCode && (
  88. <span style={{
  89. display: 'inline-flex', width: 64, height: 20, justifyContent: 'center',
  90. alignItems: 'center', background: '#E8FCF6', borderRadius: 4, fontSize: 12, color: '#009966'
  91. }}>{
  92. (operationLevel.filter((a) => a.value == operationLevelCode)).length > 0 ? (operationLevel.filter((a) => a.value == operationLevelCode))[0].name : ''
  93. }</span>
  94. )
  95. }
  96. {
  97. (!operationFlag && !techFlag) && (<Fragment />)
  98. }
  99. </div>
  100. },
  101. },
  102. {
  103. title: '操作',
  104. key: 'option',
  105. width: 80,
  106. fixed: 'right',
  107. valueType: 'option',
  108. render: (_: any, record: any) => {
  109. return <a key={1} onClick={() => {
  110. set_currentEditRow(record);
  111. set_drawerVisible(true)
  112. }}>详情</a>
  113. },
  114. },
  115. ];
  116. const getTableData = async (params: any) => {
  117. const resp = await getTableDataReq({ ...params });
  118. if (resp) {
  119. return {
  120. data: resp.list,
  121. success: true
  122. }
  123. } else {
  124. return []
  125. }
  126. }
  127. const getOperationLevelList = async () => {
  128. const resp = await getDicDataBySysId(KcimCenterSysId, 'SURGICAL_AND_OPERATIONAL_LEVELS');
  129. if (resp) {
  130. set_operationLevel(resp.dataVoList);
  131. }
  132. }
  133. const updateTable = async (formVal: any, type: 'ADD' | 'EDIT') => {
  134. try {
  135. const { files, memo, id } = formVal;
  136. let result = {};
  137. if (files[0].response) {
  138. const { name, response: { data: { downUrl } } } = files[0];
  139. result = {
  140. id: type == 'EDIT' ? id : null,
  141. userId: userInfo.id,
  142. fileName: name,
  143. url: downUrl,
  144. description: memo
  145. };
  146. } else {
  147. const { name ,url} = files[0];
  148. result = {
  149. id: type == 'EDIT' ? id : null,
  150. userId: userInfo.id,
  151. fileName: name,
  152. url: url,
  153. description: memo
  154. };
  155. }
  156. const resp = type == 'ADD' ? await addData(result) : await editData(result);
  157. if (resp) {
  158. set_currentEditAttachment(undefined);
  159. getUserInfo();
  160. set_modalFormVisible(false);
  161. message.success('操作成功!');
  162. }
  163. return true;
  164. } catch (error) {
  165. console.log({ error });
  166. return false;
  167. }
  168. }
  169. const tableDataSearchHandle = (key: string) => {
  170. set_tableDataFilterParams({ ...tableDataFilterParams, current: 1, [key]: tableDataSearchKeywords })
  171. }
  172. const getAuthPeriod = (code: number) => {
  173. const result = authTimeType.filter(a => a.value == code);
  174. return result.length > 0 ? result[0].label : '-'
  175. }
  176. const getUserInfo = async () => {
  177. const resp = await getMyInfoReq();
  178. if (resp) {
  179. set_userInfo(resp);
  180. }
  181. }
  182. const delUseerAttachmentHandle = async (record: any) => {
  183. const resp = await delData(record.id);
  184. if (resp) {
  185. getUserInfo();
  186. set_modalFormVisible(false);
  187. message.success('操作成功!');
  188. }
  189. }
  190. const drapDownItems = (fileData: any) => [
  191. {
  192. key: '1',
  193. label: (
  194. <a onClick={() => {
  195. set_currentEditAttachment(fileData);
  196. set_modalFormVisible(true);
  197. }}>编辑</a>
  198. ),
  199. },
  200. {
  201. key: '2',
  202. label: (
  203. <Popconfirm
  204. title="是否确认删除?"
  205. key="del"
  206. onConfirm={() => delUseerAttachmentHandle(fileData)}
  207. >
  208. <a>删除</a>
  209. </Popconfirm>
  210. )
  211. },
  212. ]
  213. useEffect(() => {
  214. if (drawerVisible) {
  215. getOperationLevelList();
  216. }
  217. }, [drawerVisible]);
  218. useEffect(()=>{
  219. if(!modalFormVisible){
  220. set_currentEditAttachment(undefined);
  221. }
  222. },[modalFormVisible]);
  223. useEffect(() => {
  224. getUserInfo();
  225. }, []);
  226. return (
  227. <KCIMPagecontainer className='MyQualifications' title={false} >
  228. <ModalForm
  229. title={currentEditAttachment ? '编辑附件' : '上传附件'}
  230. width={352}
  231. formRef={formRef}
  232. open={modalFormVisible}
  233. initialValues={currentEditAttachment ? {
  234. files: [{ uid: currentEditAttachment.id, name: currentEditAttachment.fileName, url: currentEditAttachment.url, status: 'done' }],
  235. memo: currentEditAttachment.description
  236. } : {}}
  237. onFinish={(val) => {
  238. return updateTable(currentEditAttachment ? { ...currentEditAttachment, ...val } : { ...val }, currentEditAttachment ? 'EDIT' : 'ADD');
  239. }}
  240. modalProps={{ destroyOnClose: true, onCancel: () => set_modalFormVisible(false), }}
  241. colProps={{ span: 24 }}
  242. grid
  243. submitter={{
  244. searchConfig: {
  245. submitText: '确认',
  246. resetText: '取消',
  247. },
  248. }}
  249. >
  250. <ProFormUploadDragger
  251. name="files" // 表单字段名
  252. action={'/gateway/centerSys/api/upload'}
  253. max={1} // 最大上传文件数
  254. description={false}
  255. title={<span style={{ fontSize: 14, color: '#17181A' }}>点击或将文件拖拽到这里上传</span>}
  256. fieldProps={{
  257. name: 'file',
  258. height: 140,
  259. multiple: false,
  260. headers: {
  261. token: initialState?.userData.token as string
  262. },
  263. // onChange(info) {
  264. // const { status } = info.file;
  265. // if (status !== 'uploading') {
  266. // console.log(info.file, info.fileList);
  267. // }
  268. // if (status === 'done') {
  269. // message.success(`${info.file.name} 文件上传成功。`);
  270. // } else if (status === 'error') {
  271. // message.error(`${info.file.name} 文件上传失败。`);
  272. // }
  273. // },
  274. // onDrop(e) {
  275. // console.log('拖拽上传的文件:', e.dataTransfer.files);
  276. // },
  277. }}
  278. />
  279. <ProFormTextArea name={'memo'} fieldProps={{
  280. style: { height: 85 }
  281. }} label='附件说明:' />
  282. </ModalForm>
  283. <Drawer destroyOnClose={true} className='MyQualifications-authDetailDrawer' width={700}
  284. open={drawerVisible} headerStyle={{ display: 'none' }} bodyStyle={{ background: '#F5F7FA', padding: 16 }}
  285. >
  286. <div className='authDetailDrawer-topBar'>
  287. <div className='authDetailDrawer-topBar-title'>
  288. <div className='closeIcon' onClick={() => set_drawerVisible(false)}><IconFont type={'iconquxiao'} /></div>{'详情'}
  289. </div>
  290. </div>
  291. <div className='authDetailDrawer-content'>
  292. <div className='authDetailDrawer-info'>
  293. <div className='authDetailDrawer-info-title'>
  294. {currentEditRow?.qualification?.name}
  295. {
  296. currentEditRow?.qualification?.techFlag == 1 && (
  297. <span style={{
  298. display: 'inline-flex', height: 20, justifyContent: 'center', marginLeft: 8, padding: '0 12px',
  299. alignItems: 'center', background: '#FFF5EB', borderRadius: 4, fontSize: 12, color: '#FF8000', marginRight: 4
  300. }}>医疗技术</span>
  301. )
  302. }
  303. {
  304. currentEditRow?.qualification?.operationFlag == 1 && (
  305. <span style={{
  306. display: 'inline-flex', height: 20, justifyContent: 'center', padding: '0 12px', marginLeft: 8,
  307. alignItems: 'center', background: '#E8FCF6', borderRadius: 4, fontSize: 12, color: '#009966'
  308. }}>{
  309. ((operationLevel.filter((a) => a.value == currentEditRow?.qualification?.operationLevelCode))).length > 0 ? ((operationLevel.filter((a) => a.value == currentEditRow?.qualification?.operationLevelCode)))[0].name : ''
  310. }</span>
  311. )
  312. }
  313. </div>
  314. <div className='authDetailDrawer-info-title-sub'>
  315. 资质编码:{currentEditRow?.qualification?.code}<span style={{ padding: '0 8px' }}></span>
  316. 资质分类:{currentEditRow?.qualification?.qualificationTypeName}
  317. {currentEditRow?.qualification?.standard.length > 0 && (
  318. <div className='authDetailDrawer-info-standard'>
  319. {currentEditRow.qualification.standard}
  320. <img src={require('../../../../static/shouquanyiju.png')} alt="" />
  321. </div>
  322. )}
  323. </div>
  324. </div>
  325. <div className='authHistory'>
  326. <div className='authHistory-title'>授权记录</div>
  327. <div className='historyListWrapper'>
  328. {
  329. currentEditRow?.applyList?.map((a: any, index: number) => {
  330. return <div className='historyList' key={index}>
  331. <img src={require(`../../../../static/${index == 0 ? 'gou_black' : 'gou_white'}.png`)} alt="" />
  332. <div className='historyList-detail'>
  333. {(authTimeType.filter((b) => b.value == a.qualificationPeriod)).length > 0 ? (authTimeType.filter((b) => b.value == a.qualificationPeriod))[0].label : ''}
  334. |{`${a.beginDate}至${a.endDate}`}
  335. {a.applyAdjust.length > 0 && (
  336. <Popover className='qualificationAuth-popover' overlayInnerStyle={{borderRadius:4}} content={<AuthHisContent hisList={[...a.applyAdjust]} />} title={false} >
  337. <IconFont type={'icon-qingliangtishi'} style={{ marginLeft: 10, cursor: 'pointer' }} />
  338. </Popover>
  339. )}
  340. </div>
  341. <div className={`status ${a.currentStatus == '授权中'?'authing':'expired'}`}>{a.currentStatus}</div>
  342. </div>
  343. })
  344. }
  345. </div>
  346. </div>
  347. </div>
  348. </Drawer>
  349. <div className='pageContent'>
  350. <div className='left'>
  351. <div className='pageContent-title'>我的资质</div>
  352. <div className='toolBar'>
  353. <div className='filter'>
  354. <div className='filterItem'>
  355. <span className='label'>手术级别:</span>
  356. <ProFormSelect noStyle
  357. style={{ width: 160, marginRight: 16 }}
  358. placeholder={'请选择'}
  359. request={async () => {
  360. const resp = await getDicDataBySysId(KcimCenterSysId, 'SURGICAL_AND_OPERATIONAL_LEVELS');
  361. if (resp) {
  362. set_operationLevel(resp.dataVoList);
  363. return resp.dataVoList.map((item: any) => ({ label: item.name, value: item.value }))
  364. }
  365. }}
  366. fieldProps={{
  367. onChange(value, option) {
  368. set_tableDataFilterParams({ ...tableDataFilterParams, current: 1, operationLevelCode: value })
  369. },
  370. }}
  371. />
  372. </div>
  373. <div className='filterItem'>
  374. <span className='label'>检索:</span>
  375. <Input placeholder={'资质名称'} style={{ width: 160, marginRight: 16 }} allowClear autoComplete='off'
  376. suffix={
  377. <IconFont style={{ color: '#99A6BF' }} type="iconsousuo" onClick={() => tableDataSearchHandle('name')} />
  378. }
  379. onChange={(e) => {
  380. set_tableDataSearchKeywords(e.target.value);
  381. if (e.target.value.length == 0) {
  382. set_tableDataSearchKeywords('');
  383. set_tableDataFilterParams({ ...tableDataFilterParams, name: undefined })
  384. }
  385. }}
  386. onPressEnter={(e) => {
  387. tableDataSearchHandle('name')
  388. }}
  389. />
  390. </div>
  391. <div className='filterItem'>
  392. <ProFormCheckbox.Group
  393. name="checkbox"
  394. layout='horizontal'
  395. noStyle
  396. options={['医疗技术', '手术']}
  397. fieldProps={{
  398. onChange(checkedValue) {
  399. set_tableDataFilterParams({
  400. ...tableDataFilterParams,
  401. current: 1,
  402. techFlag: checkedValue.findIndex((a) => a == '医疗技术') != -1 ? 1 : 0,
  403. operationFlag: checkedValue.findIndex((a) => a == '手术') != -1 ? 1 : 0,
  404. })
  405. },
  406. }}
  407. />
  408. </div>
  409. </div>
  410. {currentSelectedTableRows.length > 0 && (
  411. <div className='btnGroup'>
  412. <span className='batchAdjust' onClick={() => set_modalFormVisible(true)}>批量调整</span>
  413. </div>
  414. )}
  415. </div>
  416. <div style={{ marginTop: 16 }}>
  417. <KCIMTable scroll={{ y: `calc(100vh - 230px)` }}
  418. actionRef={tableRef} columns={columns as ProColumns[]} rowKey='id'
  419. params={tableDataFilterParams}
  420. request={(params) => getTableData(params)}
  421. // dataSource={showList}
  422. tableAlertRender={false}
  423. />
  424. </div>
  425. </div>
  426. <div className='right'>
  427. <div className='avatar-info'>
  428. <img src={require('../../../../static/avatar.png')} alt="" />
  429. <div className='name'>
  430. <div className='name-text'>{userInfo?.name}{userInfo?.gender&&<img src={require(`../../../../static/${userInfo?.gender == '男' ? 'male' : 'female'}.png`)} alt="" />}</div>
  431. <div className='id'>{userInfo?.id}</div>
  432. </div>
  433. </div>
  434. <div className='jobInfo'>
  435. <div className='jobInfo-block'>
  436. <div className='value'>{userInfo?.deptName??'-'}</div>
  437. <div className='label'>科室</div>
  438. </div>
  439. <div className='jobInfo-block'>
  440. <div className='value'>{userInfo?.title??'-'}</div>
  441. <div className='label'>职称</div>
  442. </div>
  443. <div className='jobInfo-block'>
  444. <div className='value'>{userInfo?.jobTitle??'-'}</div>
  445. <div className='label'>职务</div>
  446. </div>
  447. </div>
  448. <div className='qualifi-info-title'><span>资质信息</span></div>
  449. <div className='qualifi-info-detail'>
  450. <div className='qualifi-info-list'>资格证号:<span>{userInfo?.qualificationCertificateNo??'-'}</span></div>
  451. <div className='qualifi-info-list'>执业类别:<span>{userInfo?.practiceCate??'-'}</span></div>
  452. <div className='qualifi-info-list'>执业证号:<span>{userInfo?.practiceCertificateNo??'-'}</span></div>
  453. <div className='qualifi-info-list'>执业专业:<span>{userInfo?.major??'-'}</span></div>
  454. <div className='qualifi-info-list'>医师级别:<span>{userInfo?.doctorLevel??'-'}</span></div>
  455. </div>
  456. <div className='attachment-title'><span>相关附件</span><a onClick={() => set_modalFormVisible(true)}>上传</a></div>
  457. <div className='attachment-detail'>
  458. {
  459. userInfo?.attachments.map((a: any, index: number) => (
  460. <div className='attachment-detail-list' key={index}>
  461. <img src={require('../../../../static/fileIcon.png')} alt="" />
  462. <div className='attachment-detail-list-info'>
  463. <div className='attachment-list-name'>{a.fileName}</div>
  464. <div className='attachment-list-subText'>{a.description}</div>
  465. </div>
  466. <Dropdown trigger={['hover']} menu={{ items: drapDownItems(a) }}><span className='more'>...</span></Dropdown>
  467. </div>
  468. ))
  469. }
  470. </div>
  471. </div>
  472. </div>
  473. </KCIMPagecontainer>
  474. )
  475. }