Browse Source

🚀 架构重构

master
wangxiang 3 years ago
parent
commit
e8b0767370
  1. 51
      src/api/platform/common/controller/boxcard.ts
  2. 40
      src/api/platform/common/controller/doctor.ts
  3. 59
      src/api/platform/common/controller/hospital.ts
  4. 52
      src/api/platform/common/controller/institution.ts
  5. 59
      src/api/platform/common/controller/office.ts
  6. 20
      src/api/platform/common/controller/org.ts
  7. 19
      src/api/platform/common/controller/project.ts
  8. 33
      src/api/platform/common/controller/report.ts
  9. 43
      src/api/platform/common/entity/doctor.ts
  10. 50
      src/api/platform/common/entity/hospital.ts
  11. 39
      src/api/platform/common/entity/institution.ts
  12. 41
      src/api/platform/common/entity/office.ts
  13. 22
      src/api/platform/common/entity/org.ts
  14. 15
      src/api/platform/common/entity/project.ts
  15. 49
      src/api/platform/common/entity/report.ts
  16. 30
      src/api/platform/system/entity/project.ts
  17. 3
      src/components/Form/index.ts
  18. 158
      src/components/Form/src/BasicForm.vue
  19. 8
      src/components/Form/src/componentMap.ts
  20. 198
      src/components/Form/src/components/ApiCascader.vue
  21. 134
      src/components/Form/src/components/ApiRadioGroup.vue
  22. 23
      src/components/Form/src/components/ApiSelect.vue
  23. 90
      src/components/Form/src/components/ApiTree.vue
  24. 18
      src/components/Form/src/components/ApiTreeSelect.vue
  25. 4
      src/components/Form/src/components/FormAction.vue
  26. 182
      src/components/Form/src/components/FormItem.vue
  27. 4
      src/components/Form/src/helper.ts
  28. 25
      src/components/Form/src/hooks/useAdvanced.ts
  29. 11
      src/components/Form/src/hooks/useAutoFocus.ts
  30. 14
      src/components/Form/src/hooks/useForm.ts
  31. 96
      src/components/Form/src/hooks/useFormEvents.ts
  32. 64
      src/components/Form/src/hooks/useFormValues.ts
  33. 8
      src/components/Form/src/hooks/useLabelWidth.ts
  34. 3
      src/components/Form/src/props.ts
  35. 21
      src/components/Form/src/types/form.ts
  36. 6
      src/components/Form/src/types/index.ts
  37. 5
      src/main.ts
  38. 57
      src/views/common/boxcard/BoxCardModal.vue
  39. 160
      src/views/common/boxcard/boxcard.data.ts
  40. 94
      src/views/common/boxcard/index.vue
  41. 9
      src/views/common/doctor/DoctorModal.vue
  42. 473
      src/views/common/doctor/doctor.data.ts
  43. 5
      src/views/common/doctor/index.vue
  44. 117
      src/views/common/hospital/HospitalModal.vue
  45. 462
      src/views/common/hospital/hospital.data.ts
  46. 174
      src/views/common/hospital/index.vue
  47. 136
      src/views/common/institution/index.vue
  48. 4
      src/views/common/office/OfficeModal.vue
  49. 389
      src/views/common/office/office.data.ts
  50. 10
      src/views/common/org/OrgModal.vue
  51. 136
      src/views/common/org/index.vue
  52. 0
      src/views/common/org/org.data.ts
  53. 6
      src/views/common/project/ProjectModal.vue
  54. 4
      src/views/common/project/index.vue
  55. 0
      src/views/common/project/project.data.ts
  56. 79
      src/views/common/report/ReportModal.vue
  57. 256
      src/views/common/report/index.vue
  58. 430
      src/views/common/report/report.data.ts
  59. 1
      tsconfig.json

51
src/api/platform/common/controller/boxcard.ts

@ -1,51 +0,0 @@
import {BoxCardParams, BoxCardListItem, BoxCardListGetListResult} from '/@/api/platform/common/entity/boxCard';
import { defHttp } from '/@/utils/http/axios';
import {isDef} from '/@/utils/is';
const prefix = '/iot/equip';
enum Api {
GetCardById = '/boxcard/getById',
List = '/boxcard/list',
ListForSelect = '/boxcard/listForSelect',
Add = '/boxcard/add',
Update = '/boxcard/update',
Remove = '/boxcard/remove'
}
/**
* id
* @param params
*/
export const getCardById = (params: { id: String } ) =>
defHttp.get<BoxCardListItem>({ url: prefix + Api.GetCardById, params });
/**
* list
* @param params
*/
export const cardList = (params: BoxCardParams) =>
defHttp.get<BoxCardListGetListResult>({ url: prefix + Api.List, params });
/**
* list
* @param params
*/
export const listForSelect = (params: {isUsed: number}) =>
defHttp.get<BoxCardListItem>({url: prefix + Api.ListForSelect});
/**
*
* @param params
*/
export const set = (params: BoxCardListItem) =>{
if (isDef(params.id)) {
return defHttp.put<BoxCardListItem>({ url: prefix + Api.Update, params });
} else {
return defHttp.post<BoxCardListItem>({ url: prefix + Api.Add, params });
}
};
export const remove = (params: {ids: String}) =>
defHttp.delete<boolean>({url: prefix + Api.Remove + `/${params.ids}`});

40
src/api/platform/common/controller/doctor.ts

@ -1,33 +1,25 @@
import {DoctorParams,DoctorItem,DoctorItemListResult} from '/@/api/platform/common/entity/doctor'; /**
* api模板规范代码参考,
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-
*/
import { DoctorParams, Doctor, DoctorResult } from '/@/api/platform/common/entity/doctor';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import {isDef} from '/@/utils/is';
enum Api { enum Api {
QueryById = '/system_proxy/system/doctor/query', list = '/common_proxy/common/doctor/list',
list = '/system_proxy/system/doctor/list', get = '/common_proxy/common/doctor',
add = '/system_proxy/system/doctor/add', save = '/common_proxy/common/doctor/save',
edit = '/system_proxy/system/doctor/update', edit = '/common_proxy/common/doctor/update',
del = '/system_proxy/system/doctor/remove', del = '/common_proxy/common/doctor/remove'
get = '/system_proxy/system/doctor'
} }
export const queryById = (params: { id: String }) => export const listDoctor = (params?: Partial<DoctorParams>) => defHttp.get<DoctorResult>({ url: Api.list, params }, { isReturnResultResponse: true });
defHttp.get<DoctorItem>({url: Api.QueryById + `/${params.id}`});
/** 查询医生列表 */
export const listDoctor = (params?: Partial<DoctorItem>) => defHttp.get<DoctorItemListResult>({ url: Api.list, params }, { isReturnResultResponse: true });
/** 新增医生*/ export const addDoctor = (params: Partial<Doctor>) => defHttp.post({ url: Api.save, data: params });
export const addDoctor = (params: Partial<DoctorParams>) => defHttp.post({ url: Api.add, data: params });
/** 修改医生 */ export const editDoctor = (params: Partial<Doctor>) => defHttp.put({ url: Api.edit, data: params });
export const editDoctor = (params: Partial<DoctorParams>) => defHttp.put({ url: Api.edit, data: params });
/** 查询医生详细 */ export const getDoctor = (id: string) => defHttp.get<Doctor>({ url: `${Api.get}/${id}` });
export const getDoctor = (id: string) => defHttp.get<DoctorItem>({ url: `${Api.get}/${id}` });
/** 删除医生 */ export const delDoctor = (ids: string) => defHttp.delete({ url: `${Api.del}/${ids}` });
export const delDoctor = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });

59
src/api/platform/common/controller/hospital.ts

@ -1,46 +1,25 @@
import {HospitalParams,HospitalItem,HospitalItemListResult} from '/@/api/platform/common/entity/hospital'; /**
* api模板规范代码参考,
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-
*/
import { HospitalParams, Hospital, HospitalResult } from '/@/api/platform/common/entity/hospital';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import {isDef} from '/@/utils/is';
enum Api { enum Api {
QueryById = '/system_proxy/system/hospital/query', list = '/common_proxy/common/hospital/list',
list = '/system_proxy/system/hospital/list', get = '/common_proxy/common/hospital',
add = '/system_proxy/system/hospital/add', save = '/common_proxy/common/hospital/save',
edit = '/system_proxy/system/hospital/update', edit = '/common_proxy/common/hospital/update',
del = '/system_proxy/system/hospital/remove', del = '/common_proxy/common/hospital/remove'
get = '/system_proxy/system/hospital'
} }
// export const queryById = (params: { id: String }) => export const listHospital = (params: Partial<HospitalParams>) => defHttp.get<HospitalResult>({url: Api.list, params}, { isReturnResultResponse: true });
// defHttp.get<HospitalItem>({url: Api.QueryById, params});
// export const addHospital = (params: Partial<Hospital>)=> defHttp.post({url: Api.save ,data: params});
// export const list = (params: HospitalParams) =>
// defHttp.get<HospitalItemListResult>({url: Api.list, params}); export const editHospital = (params: Partial<Hospital>) => defHttp.put({url: Api.edit, data: params});
//
// export const set = (params: HospitalItem) => { export const getHospital = (id: string) => defHttp.get<Hospital>({url: `${Api.get}/${id}` });
// if (isDef(params.id)){
// defHttp.put<HospitalItem>({url: Api.edit, params});
// }else {
// defHttp.post<HospitalItem>({url: Api.add, params});
// }
// };
// export const remove = (params: {ids: String}) => export const delHospital = (ids: string) => defHttp.delete({ url: `${Api.del}/${ids}` });
// defHttp.delete<boolean>({url: Api.Remove + `/${params.ids}`});
// export const remove = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });
export const queryById = (params: { id: String }) =>
defHttp.get<HospitalItem>({url: Api.QueryById, params});
/** 查询医院列表*/
export const list = (params: HospitalParams) =>
defHttp.get<HospitalItemListResult>({url: Api.list, params});
// export const list = (params?:Partial<HospitalItem>) =>defHttp.get<HospitalItemListResult>({url: Api.list, params},{isReturnResultResponse: true});
/** 新增医院*/
export const addHospital = (params: Partial<HospitalParams>)=> defHttp.post({url: Api.add ,data: params});
/** 修改医院*/
export const editHospital = (params: Partial<HospitalParams>) => defHttp.put({url: Api.edit, data: params});
/** 查询医院详细*/
export const getHospital = (id: string) => defHttp.get<HospitalItem>({url: `${Api.get}/${id}` });
/** 删除项目 */
export const delHospital = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });

52
src/api/platform/common/controller/institution.ts

@ -1,52 +0,0 @@
import {InstitutionParams,InstitutionItem,InstitutionItemListResult} from '/@/api/platform/common/entity/institution';
import { defHttp } from '/@/utils/http/axios';
import {isDef} from '/@/utils/is';
enum Api {
QueryById = '/system_proxy/system/institution/query',
list = '/system_proxy/system/institution/list',
add = '/system_proxy/system/institution/add',
edit = '/system_proxy/system/institution/update',
del = '/system_proxy/system/institution/remove',
get = '/system_proxy/system/institution'
}
// export const queryById = (params: { id: String }) =>
// defHttp.get<InstitutionItem>({url: prefix + Api.QueryById, params});
//
// export const list = (params: InstitutionParams) =>
// defHttp.get<InstitutionItemListResult>({url: prefix + Api.List, params});
//
// export const set = (params: InstitutionItem) => {
// if (isDef(params.id)){
// defHttp.put<InstitutionItem>({url: prefix + Api.Update, params});
// }else {
// defHttp.post<InstitutionItem>({url: prefix + Api.Add, params});
// }
// };
//
// export const remove = (params: {ids: String}) =>
// defHttp.delete<boolean>({url: prefix + Api.Remove + `/${params.ids}`});
export const queryById = (params: { id: String }) =>
defHttp.get<InstitutionItem>({url: Api.QueryById + `/${params.id}`});
/** 查询机构列表 */
// export const list = (params?: Partial<InstitutionItem>) => defHttp.get<InstitutionItemListResult>({ url: Api.list, params }, { isReturnResultResponse: true });
export const list = (params: InstitutionParams) =>
defHttp.get<InstitutionItemListResult>({url: Api.list, params});
/** 新增机构*/
export const addInstitution = (params: Partial<InstitutionParams>) => defHttp.post({ url: Api.add, data: params });
/** 修改机构 */
export const editInstitution = (params: Partial<InstitutionParams>) => defHttp.put({ url: Api.edit, data: params });
/** 查询机构详细 */
export const getInstitution = (id: string) => defHttp.get<InstitutionItem>({ url: `${Api.get}/${id}` });
/** 删除机构 */
export const delInstitution= (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });

59
src/api/platform/common/controller/office.ts

@ -1,50 +1,25 @@
import {OfficeParams,OfficeItem,OfficeItemListResult} from '/@/api/platform/common/entity/office'; /**
* api模板规范代码参考,
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-
*/
import { OfficeParams, Office, OfficeResult } from '/@/api/platform/common/entity/office';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import {isDef} from '/@/utils/is';
import {HospitalItemListResult, HospitalParams} from "/@/api/platform/common/entity/hospital";
enum Api { enum Api {
QueryById = '/system_proxy/system/office/getById', list = '/common_proxy/common/office/list',
list = '/system_proxy/system/office/list', save = '/common_proxy/common/office/save',
add = '/system_proxy/system/office/add', edit = '/common_proxy/common/office/update',
edit = '/system_proxy/system/office/update', del = '/common_proxy/common/office/remove',
del = '/system_proxy/system/office/remove', get = '/common_proxy/common/office'
get = '/system_proxy/system/office'
} }
//
// export const queryById = (params: { id: String }) =>
// defHttp.get<OfficeItem>({url:Api.QueryById, params});
//
// export const list = (params: OfficeParams) =>
// defHttp.get<OfficeItemListResult>({url: Api.List, params});
//
// export const set = (params: OfficeItem) => {
// if (isDef(params.id)){
// defHttp.put<OfficeItem>({url:Api.Update, params});
// }else {
// defHttp.post<OfficeItem>({url: Api.Add, params});
// }
// };
//
// export const remove = (params: {ids: String}) =>
// defHttp.delete<boolean>({url:Api.Remove + `/${params.ids}`});
export const queryById = (params: { id: String }) =>
defHttp.get<OfficeItem>({url: Api.QueryById + `/${params.id}`});
/** 查询科室列表 */ export const listOffice = (params?: Partial<OfficeParams>) => defHttp.get<OfficeResult>({url: Api.list, params}, { isReturnResultResponse: true });
// export const listOffice = (params?: Partial<OfficeItem>) => defHttp.get<OfficeItemListResult>({ url: Api.list, params }, { isReturnResultResponse: true });
export const listOffice = (params: OfficeItem) => export const addOffice = (params: Partial<Office>) => defHttp.post({ url: Api.save, data: params });
defHttp.get<OfficeItemListResult>({url: Api.list, params});
/** 新增科室*/
export const addOffice = (params: Partial<OfficeParams>) => defHttp.post({ url: Api.add, data: params });
/** 修改科室 */ export const editOffice = (params: Partial<Office>) => defHttp.put({ url: Api.edit, data: params });
export const editOffice = (params: Partial<OfficeParams>) => defHttp.put({ url: Api.edit, data: params });
/** 查询科室详细 */ export const getOffice = (id: string) => defHttp.get<Office>({ url: `${Api.get}/${id}` });
export const getOffice = (id: string) => defHttp.get<OfficeItem>({ url: `${Api.get}/${id}` });
/** 删除科室 */ export const delOffice = (ids: string) => defHttp.delete({ url: `${Api.del}/${ids}` });
export const delOffice = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });

20
src/api/platform/common/controller/org.ts

@ -0,0 +1,20 @@
import { OrgParams, Org, OrgResult } from '/@/api/platform/common/entity/org';
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/common_proxy/common/org/list',
get = '/common_proxy/common/org',
save = '/common_proxy/common/org/save',
edit = '/common_proxy/common/org/update',
del = '/common_proxy/common/org/remove'
}
export const listOrg = (params: OrgParams) => defHttp.get<OrgResult>({url: Api.list, params}, { isReturnResultResponse: true });
export const addOrg = (params: Partial<Org>) => defHttp.post({ url: Api.save, data: params });
export const editOrg = (params: Partial<Org>) => defHttp.put({ url: Api.edit, data: params });
export const getOrg = (id: string) => defHttp.get<Org>({ url: `${Api.get}/${id}` });
export const delOrg = (ids: string) => defHttp.delete({ url: `${Api.del}/${ids}` });

19
src/api/platform/system/controller/project.ts → src/api/platform/common/controller/project.ts

@ -3,28 +3,23 @@
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved. * Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队- * author entfrm开发团队-
*/ */
import type { ProjectParams, Project } from '../entity/project'; import type { ProjectParams, Project, ProjectResult } from '../entity/project';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
enum Api { enum Api {
list = '/system_proxy/system/project/list', list = '/common_proxy/common/project/list',
add = '/system_proxy/system/project/save', get = '/common_proxy/common/project',
get = '/system_proxy/system/project', add = '/common_proxy/common/project/save',
edit = '/system_proxy/system/project/update', edit = '/common_proxy/common/project/update',
del = '/system_proxy/system/project/remove', del = '/common_proxy/common/project/remove'
} }
/** 查询项目列表 */
export const listProject = (params?: Partial<ProjectParams>) => defHttp.get({ url: Api.list, params }); export const listProject = (params?: Partial<ProjectParams>) => defHttp.get({ url: Api.list, params });
/** 新增项目 */
export const addProject = (params: Partial<Project>) => defHttp.post({ url: Api.add, data: params }); export const addProject = (params: Partial<Project>) => defHttp.post({ url: Api.add, data: params });
/** 修改项目 */
export const editProject = (params: Partial<Project>) => defHttp.put({ url: Api.edit, data: params }); export const editProject = (params: Partial<Project>) => defHttp.put({ url: Api.edit, data: params });
/** 查询项目详细 */
export const getProject = (id: string) => defHttp.get<Project>({ url: `${Api.get}/${id}` }); export const getProject = (id: string) => defHttp.get<Project>({ url: `${Api.get}/${id}` });
/** 删除项目 */ export const delProject = (ids: string) => defHttp.delete({ url: `${Api.del}/${ids}` });
export const delProject = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });

33
src/api/platform/common/controller/report.ts

@ -1,33 +0,0 @@
import {DoctorParams,DoctorItem,DoctorItemListResult} from '/@/api/platform/common/entity/doctor';
import { defHttp } from '/@/utils/http/axios';
import {isDef} from '/@/utils/is';
enum Api {
QueryById = '/system_proxy/system/doctor/query',
list = '/system_proxy/system/doctor/list',
add = '/system_proxy/system/doctor/add',
edit = '/system_proxy/system/doctor/update',
del = '/system_proxy/system/doctor/remove',
get = '/system_proxy/system/doctor'
}
export const queryById = (params: { id: String }) =>
defHttp.get<DoctorItem>({url: Api.QueryById + `/${params.id}`});
/** 查询医生列表 */
export const listReport = (params?: Partial<DoctorItem>) => defHttp.get<DoctorItemListResult>({ url: Api.list, params }, { isReturnResultResponse: true });
/** 新增医生*/
export const addReport = (params: Partial<DoctorParams>) => defHttp.post({ url: Api.add, data: params });
/** 修改医生 */
export const editReport = (params: Partial<DoctorParams>) => defHttp.put({ url: Api.edit, data: params });
/** 查询医生详细 */
export const getReport = (id: string) => defHttp.get<DoctorItem>({ url: `${Api.get}/${id}` });
/** 删除医生 */
export const delReport = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });

43
src/api/platform/common/entity/doctor.ts

@ -1,49 +1,26 @@
// 引入基础包
import type { R } from '/#/axios'; import type { R } from '/#/axios';
import type { Page } from '/@/api/common/data/entity'; import type { Page } from '/@/api/common/data/entity';
import { CommonEntity } from '/@/api/common/data/entity';
export type DoctorParams = { /** 医生查询参数 */
name?: string; export type DoctorParams = Page & Doctor;
beginTime?: string;
endTime?: string;
} & Page;
/** 医生对象 */
export interface DoctorItem { export interface Doctor extends CommonEntity {
/**医生ID*/
id: string; id: string;
/**医生姓名*/
name: string; name: string;
/**医生职称*/
title: string; title: string;
/**医生性别*/
sex: string; sex: string;
/**医生电话*/
phone: string; phone: string;
/**医生邮箱*/
email: string; email: string;
/**地址(门牌号)*/
detailAddress: string; detailAddress: string;
/**组织类型*/ orgType: string;
organType: string; orgId: string;
/**组织id*/ orgName: string;
organId: string;
/**组织名称*/
organName: string;
/**科室ID*/
officeId: string; officeId: string;
/**科室名称*/
officeName: string; officeName: string;
/**医生状态*/
status: string; status: string;
createById: string;
createByName: string;
createTime: string;
} }
export type DoctorItemListResult = R<DoctorItem>; /** 医生响应对象 */
export type DoctorResult = R<Doctor[]>;

50
src/api/platform/common/entity/hospital.ts

@ -1,44 +1,28 @@
// 引入基础包
import type { R } from '/#/axios'; import type { R } from '/#/axios';
import type { CommonEntity,Page } from '/@/api/common/data/entity'; import type { Page } from '/@/api/common/data/entity';
import { CommonEntity } from '/@/api/common/data/entity';
export type HospitalParams = { /** 医院查询参数 */
name?: string; export type HospitalParams = Hospital & Page;
beginTime?: string;
endTime?: string;
} & Page;
export interface HospitalItem extends CommonEntity{
/** 医院对象 */
export interface Hospital extends CommonEntity {
id: string; id: string;
name: string; name: string;
type: string;
type: number; contactName: string;
contactPhone: string;
contactsName: string; contactTitle: string;
contactsTel: string;
contactsTitle: string;
payment: string; payment: string;
addressIds: string; addressIds: string;
detailAddress: string; detailAddress: string;
status: string; status: string;
mapOrientation: number;
createById: string; mapLng: number;
mapLat: number;
createByName: string; mapNotify: string;
orgIds: string[];
createTime: string;
remarks: string;
} }
export type HospitalItemListResult = R<HospitalItem>; /** 医院响应对象 */
export type HospitalResult = R<Hospital[]>;

39
src/api/platform/common/entity/institution.ts

@ -1,39 +0,0 @@
// 引入基础包
import type { R } from '/#/axios';
import type { CommonEntity,Page } from '/@/api/common/data/entity';
export type InstitutionParams = {
name?: string;
beginTime?: string;
endTime?: string;
} & Page;
export interface InstitutionItem extends CommonEntity{
id: string;
name: string;
type: string;
contactsName: string;
contactsTel: string;
contactsTitle: string;
addressId: string;
detailAddress: string;
status: string;
createById: string;
createByName: string;
createTime: string;
}
export type InstitutionItemListResult = R<InstitutionItem>;

41
src/api/platform/common/entity/office.ts

@ -1,41 +1,18 @@
// 引入基础包
import type { R } from '/#/axios'; import type { R } from '/#/axios';
import type { CommonEntity,Page } from '/@/api/common/data/entity'; import { CommonEntity, Page } from '/@/api/common/data/entity';
export type OfficeParams = { export type OfficeParams = Office & Page;
name?: string;
beginTime?: string;
endTime?: string;
} & Page;
export interface OfficeItem extends CommonEntity{
export interface Office extends CommonEntity {
id: string; id: string;
name: string; name: string;
orgType: string;
organType: string; orgId: string;
orgName: string;
organId: string; manageName: string;
managePhone: string;
organName: string;
directorName: string;
directorTel: string;
detailAddress: string; detailAddress: string;
status: string; status: string;
createById: string;
createByName: string;
createTime: string;
remarks: string;
} }
export type OfficeItemListResult = R<OfficeItem>; export type OfficeResult = R<Office[]>;

22
src/api/platform/common/entity/org.ts

@ -0,0 +1,22 @@
import type { R } from '/#/axios';
import type { CommonEntity, Page } from '/@/api/common/data/entity';
export type OrgParams = Org & Page;
export interface Org extends CommonEntity{
id: string;
name: string;
type: string;
contactName: string;
contactPhone: string;
contactTitle: string;
addressNames: string;
addressIds: string;
detailAddress: string;
status: string;
mapLng: number;
mapLat: number;
mapNotify: string;
}
export type OrgResult = R<Org[]>;

15
src/api/platform/common/entity/project.ts

@ -0,0 +1,15 @@
import type { R } from '/#/axios';
import type { TreeEntity, Page } from '/@/api/common/data/entity';
export type ProjectParams = Page & Project;
export interface Project extends TreeEntity {
code: string;
contact: string;
phone: string;
address: string;
email: string;
status: string;
}
export type ProjectResult = R<Project[]>;

49
src/api/platform/common/entity/report.ts

@ -1,49 +0,0 @@
// 引入基础包
import type { R } from '/#/axios';
import type { Page } from '/@/api/common/data/entity';
export type DoctorParams = {
name?: string;
beginTime?: string;
endTime?: string;
} & Page;
export interface DoctorItem {
/**医生ID*/
id: string;
/**医生姓名*/
name: string;
/**医生职称*/
title: string;
/**医生性别*/
sex: string;
/**医生电话*/
phone: string;
/**医生邮箱*/
email: string;
/**地址(门牌号)*/
detailAddress: string;
/**组织类型*/
organType: string;
/**组织id*/
organId: string;
/**组织名称*/
organName: string;
/**科室ID*/
officeId: string;
/**科室名称*/
officeName: string;
/**医生状态*/
status: string;
createById: string;
createByName: string;
createTime: string;
}
export type DoctorItemListResult = R<DoctorItem>;

30
src/api/platform/system/entity/project.ts

@ -1,30 +0,0 @@
/**
* @program: kicc-ui
* @description:
*
* @author: entfrm开发团队-
* @create: 2022/4/8
*/
import type { R } from '/#/axios';
import type { CommonEntity, Page } from '/@/api/common/data/entity';
/** 部门查询参数 */
export type ProjectParams = Page & Project;
/** 部门对象 */
export interface Project extends CommonEntity {
projectId: string;
code: string;
name: string;
parentId: string;
sort: number;
contacts: string;
phone: string;
address: string;
email: string;
status: string;
[key: string]: any;
}
/** 项目响应对象 */
export type ProjectResult = R<Project[]>;

3
src/components/Form/index.ts

@ -9,5 +9,8 @@ export { useForm } from './src/hooks/useForm';
export { default as ApiSelect } from './src/components/ApiSelect.vue'; export { default as ApiSelect } from './src/components/ApiSelect.vue';
export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue'; export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
export { default as ApiTree } from './src/components/ApiTree.vue';
export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
export { default as ApiCascader } from './src/components/ApiCascader.vue';
export { BasicForm }; export { BasicForm };

158
src/components/Form/src/BasicForm.vue

@ -24,12 +24,12 @@
</FormItem> </FormItem>
</template> </template>
<FormAction v-bind="{ ...getProps, ...advanceState }" @toggle-advanced="handleToggleAdvanced"> <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
<template <template
v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']" v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
#[item]="data" #[item]="data"
> >
<slot :name="item" v-bind="data"/> <slot :name="item" v-bind="data || {}"/>
</template> </template>
</FormAction> </FormAction>
<slot name="formFooter"/> <slot name="formFooter"/>
@ -53,15 +53,16 @@
import { createFormContext } from './hooks/useFormContext'; import { createFormContext } from './hooks/useFormContext';
import { useAutoFocus } from './hooks/useAutoFocus'; import { useAutoFocus } from './hooks/useAutoFocus';
import { useModalContext } from '/@/components/Modal'; import { useModalContext } from '/@/components/Modal';
import { useDebounceFn } from '@vueuse/core';
import { basicProps } from './props'; import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import type { RowProps } from 'ant-design-vue/lib/grid/Row'; import { cloneDeep } from 'lodash-es';
export default defineComponent({ export default defineComponent({
name: 'BasicForm', name: 'BasicForm',
components: { FormItem, Form, Row, FormAction }, components: { FormItem, Form, Row, FormAction },
props: basicProps, props: basicProps,
emits: ['advanced-change', 'reset', 'submit', 'register'], emits: ['advanced-change', 'reset', 'submit', 'register', 'field-value-change'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
const formModel = reactive<Recordable>({}); const formModel = reactive<Recordable>({});
const modalFn = useModalContext(); const modalFn = useModalContext();
@ -96,7 +97,7 @@
}); });
// Get uniform row style and Row configuration for the entire form // Get uniform row style and Row configuration for the entire form
const getRow = computed(() => { const getRow = computed((): Recordable => {
const { baseRowStyle = {}, rowProps } = unref(getProps); const { baseRowStyle = {}, rowProps } = unref(getProps);
return { return {
style: baseRowStyle, style: baseRowStyle,
@ -105,7 +106,7 @@
}); });
const getBindValue = computed( const getBindValue = computed(
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable) () => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
); );
const getSchema = computed((): FormSchema[] => { const getSchema = computed((): FormSchema[] => {
@ -117,7 +118,7 @@
if (!Array.isArray(defaultValue)) { if (!Array.isArray(defaultValue)) {
schema.defaultValue = dateUtil(defaultValue); schema.defaultValue = dateUtil(defaultValue);
} else { } else {
const def: moment.Moment[] = []; const def: any[] = [];
defaultValue.forEach((item) => { defaultValue.forEach((item) => {
def.push(dateUtil(item)); def.push(dateUtil(item));
}); });
@ -125,7 +126,13 @@
} }
} }
} }
return schemas as FormSchema[]; if (unref(getProps).showAdvancedButton) {
return cloneDeep(
schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[],
);
} else {
return cloneDeep(schemas as FormSchema[]);
}
}); });
const { handleToggleAdvanced } = useAdvanced({ const { handleToggleAdvanced } = useAdvanced({
@ -181,39 +188,47 @@
}); });
watch( watch(
() => unref(getProps).model, () => unref(getProps).model,
() => { () => {
const { model } = unref(getProps); const { model } = unref(getProps);
if (!model) return; if (!model) return;
setFieldsValue(model); setFieldsValue(model);
}, },
{ {
immediate: true, immediate: true,
} },
); );
watch( watch(
() => unref(getProps).schemas, () => unref(getProps).schemas,
(schemas) => { (schemas) => {
resetSchema(schemas ?? []); resetSchema(schemas ?? []);
} },
); );
watch( watch(
() => getSchema.value, () => getSchema.value,
(schema) => { (schema) => {
nextTick(() => { nextTick(() => {
// Solve the problem of modal adaptive height calculation when the form is placed in the modal // Solve the problem of modal adaptive height calculation when the form is placed in the modal
modalFn?.redoModalHeight?.(); modalFn?.redoModalHeight?.();
}); });
if (unref(isInitedDefaultRef)) { if (unref(isInitedDefaultRef)) {
return; return;
} }
if (schema?.length) { if (schema?.length) {
initDefault(); initDefault();
isInitedDefaultRef.value = true; isInitedDefaultRef.value = true;
} }
} },
);
watch(
() => formModel,
useDebounceFn(() => {
unref(getProps).submitOnChange && handleSubmit();
}, 300),
{ deep: true },
); );
async function setProps(formProps: Partial<FormProps>): Promise<void> { async function setProps(formProps: Partial<FormProps>): Promise<void> {
@ -226,6 +241,7 @@
if (!validateTrigger || validateTrigger === 'change') { if (!validateTrigger || validateTrigger === 'change') {
validateFields([key]).catch((_) => {}); validateFields([key]).catch((_) => {});
} }
emit('field-value-change', key, value);
} }
function handleEnterPress(e: KeyboardEvent) { function handleEnterPress(e: KeyboardEvent) {
@ -271,59 +287,61 @@
getProps, getProps,
formElRef, formElRef,
getSchema, getSchema,
formActionType, formActionType: formActionType as any,
setFormModel, setFormModel,
prefixCls,
getFormClass, getFormClass,
getFormActionBindProps: computed(
(): Recordable => ({ ...getProps.value, ...advanceState }),
),
...formActionType, ...formActionType,
}; };
}, },
}); });
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-form'; @prefix-cls: ~'@{namespace}-basic-form';
.@{prefix-cls} { .@{prefix-cls} {
.ant-form-item { .ant-form-item {
&-label label::after { &-label label::after {
margin: 0 6px 0 2px; margin: 0 6px 0 2px;
} }
&-with-help { &-with-help {
margin-bottom: 0; margin-bottom: 0;
} }
&:not(.ant-form-item-with-help) { &:not(.ant-form-item-with-help) {
margin-bottom: 20px; margin-bottom: 20px;
} }
&.suffix-item { &.suffix-item {
.ant-form-item-children { .ant-form-item-children {
display: flex; display: flex;
} }
.ant-form-item-control { .ant-form-item-control {
margin-top: 4px; margin-top: 4px;
} }
.suffix { .suffix {
display: inline-flex; display: inline-flex;
padding-left: 6px; padding-left: 6px;
margin-top: 1px; margin-top: 1px;
line-height: 1; line-height: 1;
align-items: center; align-items: center;
}
} }
} }
}
.ant-form-explain { .ant-form-explain {
font-size: 14px; font-size: 14px;
} }
&--compact { &--compact {
.ant-form-item { .ant-form-item {
margin-bottom: 8px !important; margin-bottom: 8px !important;
}
} }
} }
}
</style> </style>

8
src/components/Form/src/componentMap.ts

@ -18,11 +18,15 @@ import {
TreeSelect, TreeSelect,
Slider, Slider,
Rate, Rate,
Divider,
} from 'ant-design-vue'; } from 'ant-design-vue';
import ApiRadioGroup from './components/ApiRadioGroup.vue';
import RadioButtonGroup from './components/RadioButtonGroup.vue'; import RadioButtonGroup from './components/RadioButtonGroup.vue';
import ApiSelect from './components/ApiSelect.vue'; import ApiSelect from './components/ApiSelect.vue';
import ApiTree from './components/ApiTree.vue';
import ApiTreeSelect from './components/ApiTreeSelect.vue'; import ApiTreeSelect from './components/ApiTreeSelect.vue';
import ApiCascader from './components/ApiCascader.vue';
import { BasicUpload } from '/@/components/Upload'; import { BasicUpload } from '/@/components/Upload';
import { StrengthMeter } from '/@/components/StrengthMeter'; import { StrengthMeter } from '/@/components/StrengthMeter';
import { IconPicker } from '/@/components/Icon'; import { IconPicker } from '/@/components/Icon';
@ -40,13 +44,16 @@ componentMap.set('AutoComplete', AutoComplete);
componentMap.set('Select', Select); componentMap.set('Select', Select);
componentMap.set('ApiSelect', ApiSelect); componentMap.set('ApiSelect', ApiSelect);
componentMap.set('ApiTree', ApiTree);
componentMap.set('TreeSelect', TreeSelect); componentMap.set('TreeSelect', TreeSelect);
componentMap.set('ApiTreeSelect', ApiTreeSelect); componentMap.set('ApiTreeSelect', ApiTreeSelect);
componentMap.set('ApiRadioGroup', ApiRadioGroup);
componentMap.set('Switch', Switch); componentMap.set('Switch', Switch);
componentMap.set('RadioButtonGroup', RadioButtonGroup); componentMap.set('RadioButtonGroup', RadioButtonGroup);
componentMap.set('RadioGroup', Radio.Group); componentMap.set('RadioGroup', Radio.Group);
componentMap.set('Checkbox', Checkbox); componentMap.set('Checkbox', Checkbox);
componentMap.set('CheckboxGroup', Checkbox.Group); componentMap.set('CheckboxGroup', Checkbox.Group);
componentMap.set('ApiCascader', ApiCascader);
componentMap.set('Cascader', Cascader); componentMap.set('Cascader', Cascader);
componentMap.set('Slider', Slider); componentMap.set('Slider', Slider);
componentMap.set('Rate', Rate); componentMap.set('Rate', Rate);
@ -61,6 +68,7 @@ componentMap.set('IconPicker', IconPicker);
componentMap.set('InputCountDown', CountdownInput); componentMap.set('InputCountDown', CountdownInput);
componentMap.set('Upload', BasicUpload); componentMap.set('Upload', BasicUpload);
componentMap.set('Divider', Divider);
export function add(compName: ComponentType, component: Component) { export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component); componentMap.set(compName, component);

198
src/components/Form/src/components/ApiCascader.vue

@ -0,0 +1,198 @@
<template>
<a-cascader
v-model:value="state"
:options="options"
:load-data="loadData"
change-on-select
:displayRender="handleRenderDisplay"
@change="handleChange"
>
<template v-if="loading" #suffixIcon>
<LoadingOutlined spin/>
</template>
<template v-if="loading" #notFoundContent>
<span>
<LoadingOutlined spin class="mr-1"/>
{{ t('component.form.apiSelectNotFound') }}
</span>
</template>
</a-cascader>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, unref, watch, watchEffect } from 'vue';
import { Cascader } from 'ant-design-vue';
import { propTypes } from '/@/utils/propTypes';
import { isFunction } from '/@/utils/is';
import { get, omit } from 'lodash-es';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { LoadingOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
interface Option {
value: string;
label: string;
loading?: boolean;
isLeaf?: boolean;
children?: Option[];
}
export default defineComponent({
name: 'ApiCascader',
components: {
LoadingOutlined,
[Cascader.name]: Cascader,
},
props: {
value: {
type: Array,
},
api: {
type: Function as PropType<(arg?: Recordable) => Promise<Option[]>>,
default: null,
},
numberToString: propTypes.bool,
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'),
childrenField: propTypes.string.def('children'),
asyncFetchParamKey: propTypes.string.def('parentCode'),
immediate: propTypes.bool.def(true),
// init fetch params
initFetchParams: {
type: Object as PropType<Recordable>,
default: () => ({}),
},
//
isLeaf: {
type: Function as PropType<(arg: Recordable) => boolean>,
default: null,
},
displayRenderArray: {
type: Array,
},
},
emits: ['change', 'defaultChange'],
setup(props, { emit }) {
const apiData = ref<any[]>([]);
const options = ref<Option[]>([]);
const loading = ref<boolean>(false);
const emitData = ref<any[]>([]);
const isFirstLoad = ref(true);
const { t } = useI18n();
// Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props, 'value', 'change', emitData);
watch(
apiData,
(data) => {
const opts = generatorOptions(data);
options.value = opts;
},
{ deep: true },
);
function generatorOptions(options: any[]): Option[] {
const { labelField, valueField, numberToString, childrenField, isLeaf } = props;
return options.reduce((prev, next: Recordable) => {
if (next) {
const value = next[valueField];
const item = {
...omit(next, [labelField, valueField]),
label: next[labelField],
value: numberToString ? `${value}` : value,
isLeaf: isLeaf && typeof isLeaf === 'function' ? isLeaf(next) : false,
};
const children = Reflect.get(next, childrenField);
if (children) {
Reflect.set(item, childrenField, generatorOptions(children));
}
prev.push(item);
}
return prev;
}, [] as Option[]);
}
async function initialFetch() {
const api = props.api;
if (!api || !isFunction(api)) return;
apiData.value = [];
loading.value = true;
try {
const res = await api(props.initFetchParams);
if (Array.isArray(res)) {
apiData.value = res;
return;
}
if (props.resultField) {
apiData.value = get(res, props.resultField) || [];
}
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
}
}
async function loadData(selectedOptions: Option[]) {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
const api = props.api;
if (!api || !isFunction(api)) return;
try {
const res = await api({
[props.asyncFetchParamKey]: Reflect.get(targetOption, 'value'),
});
if (Array.isArray(res)) {
const children = generatorOptions(res);
targetOption.children = children;
return;
}
if (props.resultField) {
const children = generatorOptions(get(res, props.resultField) || []);
targetOption.children = children;
}
} catch (e) {
console.error(e);
} finally {
targetOption.loading = false;
}
}
watchEffect(() => {
props.immediate && initialFetch();
});
watch(
() => props.initFetchParams,
() => {
!unref(isFirstLoad) && initialFetch();
},
{ deep: true },
);
function handleChange(keys, args) {
emitData.value = keys;
emit('defaultChange', keys, args);
}
function handleRenderDisplay({ labels, selectedOptions }) {
if (unref(emitData).length === selectedOptions.length) {
return labels.join(' / ');
}
if (props.displayRenderArray) {
return props.displayRenderArray.join(' / ');
}
return '';
}
return {
state,
options,
loading,
t,
handleChange,
loadData,
handleRenderDisplay,
};
},
});
</script>

134
src/components/Form/src/components/ApiRadioGroup.vue

@ -0,0 +1,134 @@
<!--
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
-->
<template>
<RadioGroup v-model:value="state"
v-bind="attrs"
button-style="solid"
@change="handleChange"
>
<template v-for="item in getOptions" :key="`${item.value}`">
<RadioButton v-if="props.isBtn" :value="item.value" :disabled="item.disabled">
{{ item.label }}
</RadioButton>
<Radio v-else :value="item.value" :disabled="item.disabled">
{{ item.label }}
</Radio>
</template>
</RadioGroup>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, watchEffect, computed, unref, watch } from 'vue';
import { Radio } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes';
import { get, omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
export default defineComponent({
name: 'ApiRadioGroup',
components: {
RadioGroup: Radio.Group,
RadioButton: Radio.Button,
Radio,
},
props: {
api: {
type: Function as PropType<(arg?: Recordable | string) => Promise<OptionsItem[]>>,
default: null,
},
params: {
type: [Object, String] as PropType<Recordable | string>,
default: () => ({}),
},
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean>,
},
isBtn: {
type: [Boolean] as PropType<boolean>,
default: false,
},
numberToString: propTypes.bool,
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true),
},
emits: ['options-change', 'change'],
setup(props, { emit }) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
const isFirstLoad = ref(true);
const emitData = ref<any[]>([]);
const attrs = useAttrs();
const { t } = useI18n();
// Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props);
// Processing options value
const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props;
return unref(options).reduce((prev, next: Recordable) => {
if (next) {
const value = next[valueField];
prev.push({
label: next[labelField],
value: numberToString ? `${value}` : value,
...omit(next, [labelField, valueField]),
});
}
return prev;
}, [] as OptionsItem[]);
});
watchEffect(() => {
props.immediate && fetch();
});
watch(
() => props.params,
() => {
!unref(isFirstLoad) && fetch();
},
{ deep: true },
);
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) return;
options.value = [];
try {
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
}
}
function emitChange() {
emit('options-change', unref(getOptions));
}
function handleChange(_, ...args) {
emitData.value = args;
}
return { state, getOptions, attrs, loading, t, handleChange, props };
},
});
</script>

23
src/components/Form/src/components/ApiSelect.vue

@ -1,9 +1,9 @@
<template> <template>
<Select <Select
v-model:value="state" v-model:value="state"
v-bind="attrs" v-bind="$attrs"
:options="getOptions" :options="getOptions"
@dropdownVisibleChange="handleFetch" @dropdown-visible-change="handleFetch"
@change="handleChange" @change="handleChange"
> >
<template v-for="item in Object.keys($slots)" #[item]="data"> <template v-for="item in Object.keys($slots)" #[item]="data">
@ -57,6 +57,7 @@
labelField: propTypes.string.def('label'), labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'), valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true), immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false),
}, },
emits: ['options-change', 'change'], emits: ['options-change', 'change'],
setup(props, { emit }) { setup(props, { emit }) {
@ -77,9 +78,9 @@
if (next) { if (next) {
const value = next[valueField]; const value = next[valueField];
prev.push({ prev.push({
...omit(next, [labelField, valueField]),
label: next[labelField], label: next[labelField],
value: numberToString ? `${value}` : value, value: numberToString ? `${value}` : value,
...omit(next, [labelField, valueField]),
}); });
} }
return prev; return prev;
@ -87,7 +88,7 @@
}); });
watchEffect(() => { watchEffect(() => {
props.immediate && fetch(); props.immediate && !props.alwaysLoad && fetch();
}); });
watch( watch(
@ -95,7 +96,7 @@
() => { () => {
!unref(isFirstLoad) && fetch(); !unref(isFirstLoad) && fetch();
}, },
{ deep: true } { deep: true },
); );
async function fetch() { async function fetch() {
@ -121,10 +122,14 @@
} }
} }
async function handleFetch() { async function handleFetch(visible) {
if (!props.immediate && unref(isFirstLoad)) { if (visible) {
await fetch(); if (props.alwaysLoad) {
isFirstLoad.value = false; await fetch();
} else if (!props.immediate && unref(isFirstLoad)) {
await fetch();
isFirstLoad.value = false;
}
} }
} }

90
src/components/Form/src/components/ApiTree.vue

@ -0,0 +1,90 @@
<template>
<a-tree v-bind="getAttrs" @change="handleChange">
<template v-for="item in Object.keys($slots)" #[item]="data">
<slot :name="item" v-bind="data || {}"/>
</template>
<template v-if="loading" #suffixIcon>
<LoadingOutlined spin/>
</template>
</a-tree>
</template>
<script lang="ts">
import { computed, defineComponent, watch, ref, onMounted, unref } from 'vue';
import { Tree } from 'ant-design-vue';
import { isArray, isFunction } from '/@/utils/is';
import { get } from 'lodash-es';
import { propTypes } from '/@/utils/propTypes';
import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'ApiTree',
components: { ATree: Tree, LoadingOutlined },
props: {
api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
params: { type: Object },
immediate: { type: Boolean, default: true },
resultField: propTypes.string.def(''),
afterFetch: { type: Function as PropType<Fn> },
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {
const treeData = ref<Recordable[]>([]);
const isFirstLoaded = ref<Boolean>(false);
const loading = ref(false);
const getAttrs = computed(() => {
return {
...(props.api ? { treeData: unref(treeData) } : {}),
...attrs,
};
});
function handleChange(...args) {
emit('change', ...args);
}
watch(
() => props.params,
() => {
!unref(isFirstLoaded) && fetch();
},
{ deep: true },
);
watch(
() => props.immediate,
(v) => {
v && !isFirstLoaded.value && fetch();
},
);
onMounted(() => {
props.immediate && fetch();
});
async function fetch() {
const { api, afterFetch } = props;
if (!api || !isFunction(api)) return;
loading.value = true;
treeData.value = [];
let result;
try {
result = await api(props.params);
} catch (e) {
console.error(e);
}
if (afterFetch && isFunction(afterFetch)) {
result = afterFetch(result);
}
loading.value = false;
if (!result) return;
if (!isArray(result)) {
result = get(result, props.resultField);
}
treeData.value = (result as Recordable[]) || [];
isFirstLoaded.value = true;
emit('options-change', treeData.value);
}
return { getAttrs, loading, handleChange };
},
});
</script>

18
src/components/Form/src/components/ApiTreeSelect.vue

@ -43,18 +43,18 @@
} }
watch( watch(
() => props.params, () => props.params,
() => { () => {
isFirstLoaded.value && fetch(); !unref(isFirstLoaded) && fetch();
}, },
{ deep: true } { deep: true },
); );
watch( watch(
() => props.immediate, () => props.immediate,
(v) => { (v) => {
v && !isFirstLoaded.value && fetch(); v && !isFirstLoaded.value && fetch();
} },
); );
onMounted(() => { onMounted(() => {

4
src/components/Form/src/components/FormAction.vue

@ -104,7 +104,7 @@
{ {
text: t('common.resetText'), text: t('common.resetText'),
}, },
props.resetButtonOptions props.resetButtonOptions,
); );
}); });
@ -113,7 +113,7 @@
{ {
text: t('common.queryText'), text: t('common.queryText'),
}, },
props.submitButtonOptions props.submitButtonOptions,
); );
}); });

182
src/components/Form/src/components/FormItem.vue

@ -1,17 +1,16 @@
<script lang="tsx"> <script lang="tsx">
import type { PropType, Ref } from 'vue'; import type { PropType, Ref } from 'vue';
import type { FormActionType, FormProps } from '../types/form'; import { computed, defineComponent, toRefs, unref } from 'vue';
import type { FormSchema } from '../types/form'; import type { FormActionType, FormProps, FormSchema } from '../types/form';
import type { ValidationRule } from 'ant-design-vue/lib/form/Form'; import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
import type { TableActionType } from '/@/components/Table'; import type { TableActionType } from '/@/components/Table';
import { defineComponent, computed, unref, toRefs } from 'vue'; import { Col, Divider, Form } from 'ant-design-vue';
import { Form, Col } from 'ant-design-vue';
import { componentMap } from '../componentMap'; import { componentMap } from '../componentMap';
import { BasicHelp } from '/@/components/Basic'; import { BasicHelp } from '/@/components/Basic';
import { isBoolean, isFunction, isNull } from '/@/utils/is'; import { isBoolean, isFunction, isNull } from '/@/utils/is';
import { getSlot } from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
import { createPlaceholderMessage, setComponentRuleType } from '../helper'; import { createPlaceholderMessage, setComponentRuleType } from '../helper';
import { upperFirst, cloneDeep } from 'lodash-es'; import { cloneDeep, upperFirst } from 'lodash-es';
import { useItemLabelWidth } from '../hooks/useLabelWidth'; import { useItemLabelWidth } from '../hooks/useLabelWidth';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
@ -73,11 +72,17 @@
const getComponentsProps = computed(() => { const getComponentsProps = computed(() => {
const { schema, tableAction, formModel, formActionType } = props; const { schema, tableAction, formModel, formActionType } = props;
const { componentProps = {} } = schema; let { componentProps = {} } = schema;
if (!isFunction(componentProps)) { if (isFunction(componentProps)) {
return componentProps; componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
} }
return componentProps({ schema, tableAction, formModel, formActionType }) ?? {}; if (schema.component === 'Divider') {
componentProps = Object.assign({ type: 'horizontal' }, componentProps, {
orientation: 'left',
plain: true,
});
}
return componentProps as Recordable;
}); });
const getDisable = computed(() => { const getDisable = computed(() => {
@ -98,10 +103,10 @@
const { show, ifShow } = props.schema; const { show, ifShow } = props.schema;
const { showAdvancedButton } = props.formProps; const { showAdvancedButton } = props.formProps;
const itemIsAdvanced = showAdvancedButton const itemIsAdvanced = showAdvancedButton
? isBoolean(props.schema.isAdvanced) ? isBoolean(props.schema.isAdvanced)
? props.schema.isAdvanced ? props.schema.isAdvanced
: true : true
: true; : true;
let isShow = true; let isShow = true;
let isIfShow = true; let isIfShow = true;
@ -140,8 +145,8 @@
const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps; const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;
const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel')
? rulesMessageJoinLabel ? rulesMessageJoinLabel
: globalRulesMessageJoinLabel; : globalRulesMessageJoinLabel;
const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`; const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;
function validator(rule: any, value: any) { function validator(rule: any, value: any) {
@ -156,13 +161,13 @@
// //
return Promise.reject(msg); return Promise.reject(msg);
} else if ( } else if (
typeof value === 'object' && typeof value === 'object' &&
Reflect.has(value, 'checked') && Reflect.has(value, 'checked') &&
Reflect.has(value, 'halfChecked') && Reflect.has(value, 'halfChecked') &&
Array.isArray(value.checked) && Array.isArray(value.checked) &&
Array.isArray(value.halfChecked) && Array.isArray(value.halfChecked) &&
value.checked.length === 0 && value.checked.length === 0 &&
value.halfChecked.length === 0 value.halfChecked.length === 0
) { ) {
// tree // tree
return Promise.reject(msg); return Promise.reject(msg);
@ -172,12 +177,25 @@
const getRequired = isFunction(required) ? required(unref(getValues)) : required; const getRequired = isFunction(required) ? required(unref(getValues)) : required;
if ((!rules || rules.length === 0) && getRequired) { /*
rules = [{ required: getRequired, validator }]; * 1若设置了required属性又没有其他的rules就创建一个验证规则
* 2若设置了required属性又存在其他的rules则只rules中不存在required属性时才添加验证required的规则
* 也就是说rules中的required优先级大于required
*/
if (getRequired) {
if (!rules || rules.length === 0) {
rules = [{ required: getRequired, validator }];
} else {
const requiredIndex: number = rules.findIndex((rule) => Reflect.has(rule, 'required'));
if (requiredIndex === -1) {
rules.push({ required: getRequired, validator });
}
}
} }
const requiredRuleIndex: number = rules.findIndex( const requiredRuleIndex: number = rules.findIndex(
(rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator') (rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator'),
); );
if (requiredRuleIndex !== -1) { if (requiredRuleIndex !== -1) {
@ -205,8 +223,8 @@
const characterInx = rules.findIndex((val) => val.max); const characterInx = rules.findIndex((val) => val.max);
if (characterInx !== -1 && !rules[characterInx].validator) { if (characterInx !== -1 && !rules[characterInx].validator) {
rules[characterInx].message = rules[characterInx].message =
rules[characterInx].message || rules[characterInx].message ||
t('component.form.maxTip', [rules[characterInx].max] as Recordable); t('component.form.maxTip', [rules[characterInx].max] as Recordable);
} }
return rules; return rules;
} }
@ -250,7 +268,7 @@
// RangePicker place is an array // RangePicker place is an array
if (isCreatePlaceholder && component !== 'RangePicker' && component) { if (isCreatePlaceholder && component !== 'RangePicker' && component) {
propsData.placeholder = propsData.placeholder =
unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component); unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component);
} }
propsData.codeField = field; propsData.codeField = field;
propsData.formValues = unref(getValues); propsData.formValues = unref(getValues);
@ -269,8 +287,8 @@
return <Comp {...compAttr} />; return <Comp {...compAttr} />;
} }
const compSlot = isFunction(renderComponentContent) const compSlot = isFunction(renderComponentContent)
? { ...renderComponentContent(unref(getValues)) } ? { ...renderComponentContent(unref(getValues)) }
: { : {
default: () => renderComponentContent, default: () => renderComponentContent,
}; };
return <Comp {...compAttr}>{compSlot}</Comp>; return <Comp {...compAttr}>{compSlot}</Comp>;
@ -279,59 +297,67 @@
function renderLabelHelpMessage() { function renderLabelHelpMessage() {
const { label, helpMessage, helpComponentProps, subLabel } = props.schema; const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
const renderLabel = subLabel ? ( const renderLabel = subLabel ? (
<span> <span>
{label} <span class="text-secondary">{subLabel}</span> {label} <span class="text-secondary">{subLabel}</span>
</span> </span>
) : ( ) : (
label label
); );
const getHelpMessage = isFunction(helpMessage) const getHelpMessage = isFunction(helpMessage)
? helpMessage(unref(getValues)) ? helpMessage(unref(getValues))
: helpMessage; : helpMessage;
if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) { if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
return renderLabel; return renderLabel;
} }
return ( return (
<span> <span>
{renderLabel} {renderLabel}
<BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} /> <BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} />
</span> </span>
); );
} }
function renderItem() { function renderItem() {
const { itemProps, slot, render, field, suffix } = props.schema; const { itemProps, slot, render, field, suffix, component } = props.schema;
const { labelCol, wrapperCol } = unref(itemLabelWidthProp); const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
const { colon } = props.formProps; const { colon } = props.formProps;
const getContent = () => { if (component === 'Divider') {
return slot return (
? getSlot(slots, slot, unref(getValues)) <Col span={24}>
: render <Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider>
? render(unref(getValues)) </Col>
: renderComponent(); );
}; } else {
const getContent = () => {
const showSuffix = !!suffix; return slot
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix; ? getSlot(slots, slot, unref(getValues))
: render
return ( ? render(unref(getValues))
<Form.Item : renderComponent();
name={field} };
colon={colon}
class={{ 'suffix-item': showSuffix }} const showSuffix = !!suffix;
{...(itemProps as Recordable)} const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
label={renderLabelHelpMessage()}
rules={handleRules()} return (
labelCol={labelCol} <Form.Item
wrapperCol={wrapperCol} name={field}
> colon={colon}
<div style="display:flex"> class={{ 'suffix-item': showSuffix }}
<div style="flex:1">{getContent()}</div> {...(itemProps as Recordable)}
{showSuffix && <span class="suffix">{getSuffix}</span>} label={renderLabelHelpMessage()}
</div> rules={handleRules()}
</Form.Item> labelCol={labelCol}
); wrapperCol={wrapperCol}
>
<div style="display:flex">
<div style="flex:1;">{getContent()}</div>
{showSuffix && <span class="suffix">{getSuffix}</span>}
</div>
</Form.Item>
);
}
} }
return () => { return () => {
@ -347,18 +373,18 @@
const getContent = () => { const getContent = () => {
return colSlot return colSlot
? getSlot(slots, colSlot, values) ? getSlot(slots, colSlot, values)
: renderColContent : renderColContent
? renderColContent(values) ? renderColContent(values)
: renderItem(); : renderItem();
}; };
return ( return (
isIfShow && ( isIfShow && (
<Col {...realColProps} v-show={isShow}> <Col {...realColProps} v-show={isShow}>
{getContent()} {getContent()}
</Col> </Col>
) )
); );
}; };
}, },

4
src/components/Form/src/helper.ts

@ -38,7 +38,7 @@ function genType() {
export function setComponentRuleType( export function setComponentRuleType(
rule: ValidationRule, rule: ValidationRule,
component: ComponentType, component: ComponentType,
valueFormat: string valueFormat: string,
) { ) {
if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) { if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) {
rule.type = valueFormat ? 'string' : 'object'; rule.type = valueFormat ? 'string' : 'object';
@ -70,3 +70,5 @@ export function handleInputNumberValue(component?: ComponentType, val?: any) {
* *
*/ */
export const dateItemType = genType(); export const dateItemType = genType();
export const defaultValueComponents = ['Input', 'InputPassword', 'InputSearch', 'InputTextArea'];

25
src/components/Form/src/hooks/useAdvanced.ts

@ -1,6 +1,6 @@
import type { ColEx } from '../types'; import type { ColEx } from '../types';
import type { AdvanceState } from '../types/hooks'; import type { AdvanceState } from '../types/hooks';
import type { ComputedRef, Ref } from 'vue'; import { ComputedRef, getCurrentInstance, Ref } from 'vue';
import type { FormProps, FormSchema } from '../types/form'; import type { FormProps, FormSchema } from '../types/form';
import { computed, unref, watch } from 'vue'; import { computed, unref, watch } from 'vue';
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is'; import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
@ -19,13 +19,15 @@ interface UseAdvancedContext {
} }
export default function ({ export default function ({
advanceState, advanceState,
emit, emit,
getProps, getProps,
getSchema, getSchema,
formModel, formModel,
defaultValueRef, defaultValueRef,
}: UseAdvancedContext) { }: UseAdvancedContext) {
const vm = getCurrentInstance();
const { realWidthRef, screenEnum, screenRef } = useBreakpoint(); const { realWidthRef, screenEnum, screenRef } = useBreakpoint();
const getEmptySpan = computed((): number => { const getEmptySpan = computed((): number => {
@ -58,7 +60,7 @@ export default function ({
debounceUpdateAdvanced(); debounceUpdateAdvanced();
} }
}, },
{ immediate: true } { immediate: true },
); );
function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) { function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) {
@ -139,7 +141,7 @@ export default function ({
if (isShow && (colProps || baseColProps)) { if (isShow && (colProps || baseColProps)) {
const { itemColSum: sum, isAdvanced } = getAdvanced( const { itemColSum: sum, isAdvanced } = getAdvanced(
{ ...baseColProps, ...colProps }, { ...baseColProps, ...colProps },
itemColSum itemColSum,
); );
itemColSum = sum || 0; itemColSum = sum || 0;
@ -150,6 +152,9 @@ export default function ({
} }
} }
// 确保页面发送更新
vm?.proxy?.$forceUpdate();
advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpan); advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpan);
getAdvanced(unref(getProps).actionColOptions || { span: BASIC_COL_LEN }, itemColSum, true); getAdvanced(unref(getProps).actionColOptions || { span: BASIC_COL_LEN }, itemColSum, true);

11
src/components/Form/src/hooks/useAutoFocus.ts

@ -1,5 +1,6 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import type { FormSchema, FormActionType, FormProps } from '../types/form'; import type { FormSchema, FormActionType, FormProps } from '../types/form';
import { unref, nextTick, watchEffect } from 'vue'; import { unref, nextTick, watchEffect } from 'vue';
interface UseAutoFocusContext { interface UseAutoFocusContext {
@ -9,11 +10,11 @@ interface UseAutoFocusContext {
formElRef: Ref<FormActionType>; formElRef: Ref<FormActionType>;
} }
export async function useAutoFocus({ export async function useAutoFocus({
getSchema, getSchema,
getProps, getProps,
formElRef, formElRef,
isInitedDefault, isInitedDefault,
}: UseAutoFocusContext) { }: UseAutoFocusContext) {
watchEffect(async () => { watchEffect(async () => {
if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) { if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) {
return; return;

14
src/components/Form/src/hooks/useForm.ts

@ -18,7 +18,7 @@ export function useForm(props?: Props): UseFormReturnType {
const form = unref(formRef); const form = unref(formRef);
if (!form) { if (!form) {
error( error(
'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!' 'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!',
); );
} }
await nextTick(); await nextTick();
@ -27,10 +27,10 @@ export function useForm(props?: Props): UseFormReturnType {
function register(instance: FormActionType) { function register(instance: FormActionType) {
isProdMode() && isProdMode() &&
onUnmounted(() => { onUnmounted(() => {
formRef.value = null; formRef.value = null;
loadedRef.value = null; loadedRef.value = null;
}); });
if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return; if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return;
formRef.value = instance; formRef.value = instance;
@ -44,7 +44,7 @@ export function useForm(props?: Props): UseFormReturnType {
{ {
immediate: true, immediate: true,
deep: true, deep: true,
} },
); );
} }
@ -96,7 +96,7 @@ export function useForm(props?: Props): UseFormReturnType {
appendSchemaByField: async ( appendSchemaByField: async (
schema: FormSchema, schema: FormSchema,
prefixField: string | undefined, prefixField: string | undefined,
first: boolean first: boolean,
) => { ) => {
const form = await getForm(); const form = await getForm();
form.appendSchemaByField(schema, prefixField, first); form.appendSchemaByField(schema, prefixField, first);

96
src/components/Form/src/hooks/useFormEvents.ts

@ -1,10 +1,10 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import type { FormProps, FormSchema, FormActionType } from '../types/form'; import type { FormProps, FormSchema, FormActionType } from '../types/form';
import type { NamePath } from 'ant-design-vue/lib/form/interface'; import type { NamePath } from 'ant-design-vue/lib/form/interface';
import { unref, toRaw } from 'vue'; import { unref, toRaw, nextTick } from 'vue';
import { isArray, isFunction, isObject, isString } from '/@/utils/is'; import { isArray, isFunction, isObject, isString, isDef, isNullOrUnDef } from '/@/utils/is';
import { deepMerge } from '/@/utils'; import { deepMerge } from '/@/utils';
import { dateItemType, handleInputNumberValue } from '../helper'; import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper';
import { dateUtil } from '/@/utils/dateUtil'; import { dateUtil } from '/@/utils/dateUtil';
import { cloneDeep, uniqBy } from 'lodash-es'; import { cloneDeep, uniqBy } from 'lodash-es';
import { error } from '/@/utils/log'; import { error } from '/@/utils/log';
@ -20,15 +20,15 @@ interface UseFormActionContext {
handleFormValues: Fn; handleFormValues: Fn;
} }
export function useFormEvents({ export function useFormEvents({
emit, emit,
getProps, getProps,
formModel, formModel,
getSchema, getSchema,
defaultValueRef, defaultValueRef,
formElRef, formElRef,
schemaRef, schemaRef,
handleFormValues, handleFormValues,
}: UseFormActionContext) { }: UseFormActionContext) {
async function resetFields(): Promise<void> { async function resetFields(): Promise<void> {
const { resetFunc, submitOnReset } = unref(getProps); const { resetFunc, submitOnReset } = unref(getProps);
resetFunc && isFunction(resetFunc) && (await resetFunc()); resetFunc && isFunction(resetFunc) && (await resetFunc());
@ -37,9 +37,13 @@ export function useFormEvents({
if (!formEl) return; if (!formEl) return;
Object.keys(formModel).forEach((key) => { Object.keys(formModel).forEach((key) => {
formModel[key] = defaultValueRef.value[key]; const schema = unref(getSchema).find((item) => item.field === key);
const isInput = schema?.component && defaultValueComponents.includes(schema.component);
const defaultValue = cloneDeep(defaultValueRef.value[key]);
formModel[key] = isInput ? defaultValue || '' : defaultValue;
}); });
clearValidate(); nextTick(() => clearValidate());
emit('reset', toRaw(formModel)); emit('reset', toRaw(formModel));
submitOnReset && handleSubmit(); submitOnReset && handleSubmit();
} }
@ -52,6 +56,10 @@ export function useFormEvents({
.map((item) => item.field) .map((item) => item.field)
.filter(Boolean); .filter(Boolean);
// key 支持 a.b.c 的嵌套写法
const delimiter = '.';
const nestKeyArray = fields.filter((item) => item.indexOf(delimiter) >= 0);
const validKeys: string[] = []; const validKeys: string[] = [];
Object.keys(values).forEach((key) => { Object.keys(values).forEach((key) => {
const schema = unref(getSchema).find((item) => item.field === key); const schema = unref(getSchema).find((item) => item.field === key);
@ -82,6 +90,21 @@ export function useFormEvents({
formModel[key] = value; formModel[key] = value;
} }
validKeys.push(key); validKeys.push(key);
} else {
nestKeyArray.forEach((nestKey: string) => {
try {
const value = eval('values' + delimiter + nestKey);
if (isDef(value)) {
formModel[nestKey] = value;
validKeys.push(nestKey);
}
} catch (e) {
// key not exist
if (isDef(defaultValueRef.value[nestKey])) {
formModel[nestKey] = cloneDeep(defaultValueRef.value[nestKey]);
}
}
});
} }
}); });
validateFields(validKeys).catch((_) => {}); validateFields(validKeys).catch((_) => {});
@ -125,18 +148,18 @@ export function useFormEvents({
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
const index = schemaList.findIndex((schema) => schema.field === prefixField); const index = schemaList.findIndex((schema) => schema.field === prefixField);
const hasInList = schemaList.some((item) => item.field === prefixField || schema.field);
if (!hasInList) return;
if (!prefixField || index === -1 || first) { if (!prefixField || index === -1 || first) {
first ? schemaList.unshift(schema) : schemaList.push(schema); first ? schemaList.unshift(schema) : schemaList.push(schema);
schemaRef.value = schemaList; schemaRef.value = schemaList;
_setDefaultValue(schema);
return; return;
} }
if (index !== -1) { if (index !== -1) {
schemaList.splice(index + 1, 0, schema); schemaList.splice(index + 1, 0, schema);
} }
_setDefaultValue(schema);
schemaRef.value = schemaList; schemaRef.value = schemaList;
} }
@ -149,11 +172,13 @@ export function useFormEvents({
updateData = [...data]; updateData = [...data];
} }
const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); const hasField = updateData.every(
(item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field),
);
if (!hasField) { if (!hasField) {
error( error(
'All children of the form Schema array that need to be updated must contain the `field` field' 'All children of the form Schema array that need to be updated must contain the `field` field',
); );
return; return;
} }
@ -169,11 +194,13 @@ export function useFormEvents({
updateData = [...data]; updateData = [...data];
} }
const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); const hasField = updateData.every(
(item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field),
);
if (!hasField) { if (!hasField) {
error( error(
'All children of the form Schema array that need to be updated must contain the `field` field' 'All children of the form Schema array that need to be updated must contain the `field` field',
); );
return; return;
} }
@ -188,9 +215,36 @@ export function useFormEvents({
} }
}); });
}); });
_setDefaultValue(schema);
schemaRef.value = uniqBy(schema, 'field'); schemaRef.value = uniqBy(schema, 'field');
} }
function _setDefaultValue(data: FormSchema | FormSchema[]) {
let schemas: FormSchema[] = [];
if (isObject(data)) {
schemas.push(data as FormSchema);
}
if (isArray(data)) {
schemas = [...data];
}
const obj: Recordable = {};
const currentFieldsValue = getFieldsValue();
schemas.forEach((item) => {
if (
item.component != 'Divider' &&
Reflect.has(item, 'field') &&
item.field &&
!isNullOrUnDef(item.defaultValue) &&
!(item.field in currentFieldsValue)
) {
obj[item.field] = item.defaultValue;
}
});
setFieldsValue(obj);
}
function getFieldsValue(): Recordable { function getFieldsValue(): Recordable {
const formEl = unref(formElRef); const formEl = unref(formElRef);
if (!formEl) return {}; if (!formEl) return {};

64
src/components/Form/src/hooks/useFormValues.ts

@ -3,7 +3,7 @@ import { dateUtil } from '/@/utils/dateUtil';
import { unref } from 'vue'; import { unref } from 'vue';
import type { Ref, ComputedRef } from 'vue'; import type { Ref, ComputedRef } from 'vue';
import type { FormProps, FormSchema } from '../types/form'; import type { FormProps, FormSchema } from '../types/form';
import { set } from 'lodash-es'; import { cloneDeep, set } from 'lodash-es';
interface UseFormValuesContext { interface UseFormValuesContext {
defaultValueRef: Ref<any>; defaultValueRef: Ref<any>;
@ -11,12 +11,49 @@ interface UseFormValuesContext {
getProps: ComputedRef<FormProps>; getProps: ComputedRef<FormProps>;
formModel: Recordable; formModel: Recordable;
} }
/**
* @desription deconstruct array-link key. This method will mutate the target.
*/
function tryDeconstructArray(key: string, value: any, target: Recordable) {
const pattern = /^\[(.+)\]$/;
if (pattern.test(key)) {
const match = key.match(pattern);
if (match && match[1]) {
const keys = match[1].split(',');
value = Array.isArray(value) ? value : [value];
keys.forEach((k, index) => {
set(target, k.trim(), value[index]);
});
return true;
}
}
}
/**
* @desription deconstruct object-link key. This method will mutate the target.
*/
function tryDeconstructObject(key: string, value: any, target: Recordable) {
const pattern = /^\{(.+)\}$/;
if (pattern.test(key)) {
const match = key.match(pattern);
if (match && match[1]) {
const keys = match[1].split(',');
value = isObject(value) ? value : {};
keys.forEach((k) => {
set(target, k.trim(), value[k.trim()]);
});
return true;
}
}
}
export function useFormValues({ export function useFormValues({
defaultValueRef, defaultValueRef,
getSchema, getSchema,
formModel, formModel,
getProps, getProps,
}: UseFormValuesContext) { }: UseFormValuesContext) {
// Processing form values // Processing form values
function handleFormValues(values: Recordable) { function handleFormValues(values: Recordable) {
if (!isObject(values)) { if (!isObject(values)) {
@ -33,14 +70,18 @@ export function useFormValues({
if (isObject(value)) { if (isObject(value)) {
value = transformDateFunc?.(value); value = transformDateFunc?.(value);
} }
if (isArray(value) && value[0]?._isAMomentObject && value[1]?._isAMomentObject) {
if (isArray(value) && value[0]?.format && value[1]?.format) {
value = value.map((item) => transformDateFunc?.(item)); value = value.map((item) => transformDateFunc?.(item));
} }
// Remove spaces // Remove spaces
if (isString(value)) { if (isString(value)) {
value = value.trim(); value = value.trim();
} }
set(res, key, value); if (!tryDeconstructArray(key, value, res) && !tryDeconstructObject(key, value, res)) {
// 没有解构成功的,按原样赋值
set(res, key, value);
}
} }
return handleRangeTimeValue(res); return handleRangeTimeValue(res);
} }
@ -77,10 +118,13 @@ export function useFormValues({
const { defaultValue } = item; const { defaultValue } = item;
if (!isNullOrUnDef(defaultValue)) { if (!isNullOrUnDef(defaultValue)) {
obj[item.field] = defaultValue; obj[item.field] = defaultValue;
formModel[item.field] = defaultValue;
if (formModel[item.field] === undefined) {
formModel[item.field] = defaultValue;
}
} }
}); });
defaultValueRef.value = obj; defaultValueRef.value = cloneDeep(obj);
} }
return { handleFormValues, initDefault }; return { handleFormValues, initDefault };

8
src/components/Form/src/hooks/useLabelWidth.ts

@ -1,6 +1,6 @@
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import type { FormProps, FormSchema } from '../types/form';
import { computed, unref } from 'vue'; import { computed, unref } from 'vue';
import type { FormProps, FormSchema } from '../types/form';
import { isNumber } from '/@/utils/is'; import { isNumber } from '/@/utils/is';
export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<FormProps>) { export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<FormProps>) {
@ -13,6 +13,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
labelWidth: globalLabelWidth, labelWidth: globalLabelWidth,
labelCol: globalLabelCol, labelCol: globalLabelCol,
wrapperCol: globWrapperCol, wrapperCol: globWrapperCol,
layout,
} = unref(propsRef); } = unref(propsRef);
// If labelWidth is set globally, all items setting // If labelWidth is set globally, all items setting
@ -32,7 +33,10 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
return { return {
labelCol: { style: { width }, ...col }, labelCol: { style: { width }, ...col },
wrapperCol: { style: { width: `calc(100% - ${width})` }, ...wrapCol }, wrapperCol: {
style: { width: layout === 'vertical' ? '100%' : `calc(100% - ${width})` },
...wrapCol,
},
}; };
}); });
} }

3
src/components/Form/src/props.ts

@ -40,6 +40,7 @@ export const basicProps = {
// 在INPUT组件上单击回车时,是否自动提交 // 在INPUT组件上单击回车时,是否自动提交
autoSubmitOnEnter: propTypes.bool.def(false), autoSubmitOnEnter: propTypes.bool.def(false),
submitOnReset: propTypes.bool, submitOnReset: propTypes.bool,
submitOnChange: propTypes.bool,
size: propTypes.oneOf(['default', 'small', 'large']).def('default'), size: propTypes.oneOf(['default', 'small', 'large']).def('default'),
// 禁用表单 // 禁用表单
disabled: propTypes.bool, disabled: propTypes.bool,
@ -53,7 +54,7 @@ export const basicProps = {
transformDateFunc: { transformDateFunc: {
type: Function as PropType<Fn>, type: Function as PropType<Fn>,
default: (date: any) => { default: (date: any) => {
return date._isAMomentObject ? date?.format('YYYY-MM-DD HH:mm:ss') : date; return date?.format?.('YYYY-MM-DD HH:mm:ss') ?? date;
}, },
}, },
rulesMessageJoinLabel: propTypes.bool.def(true), rulesMessageJoinLabel: propTypes.bool.def(true),

21
src/components/Form/src/types/form.ts

@ -37,7 +37,7 @@ export interface FormActionType {
appendSchemaByField: ( appendSchemaByField: (
schema: FormSchema, schema: FormSchema,
prefixField: string | undefined, prefixField: string | undefined,
first?: boolean | undefined first?: boolean | undefined,
) => Promise<void>; ) => Promise<void>;
validateFields: (nameList?: NamePath[]) => Promise<any>; validateFields: (nameList?: NamePath[]) => Promise<any>;
validate: (nameList?: NamePath[]) => Promise<any>; validate: (nameList?: NamePath[]) => Promise<any>;
@ -49,17 +49,20 @@ export type RegisterFn = (formInstance: FormActionType) => void;
export type UseFormReturnType = [RegisterFn, FormActionType]; export type UseFormReturnType = [RegisterFn, FormActionType];
export interface FormProps { export interface FormProps {
name?: string;
layout?: 'vertical' | 'inline' | 'horizontal'; layout?: 'vertical' | 'inline' | 'horizontal';
// Form value // Form value
model?: Recordable; model?: Recordable;
// The width of all items in the entire form // The width of all items in the entire form
labelWidth?: number | string; labelWidth?: number | string;
//alignment // alignment
labelAlign?: 'left' | 'right'; labelAlign?: 'left' | 'right';
//Row configuration for the entire form // Row configuration for the entire form
rowProps?: RowProps; rowProps?: RowProps;
// Submit form on reset // Submit form on reset
submitOnReset?: boolean; submitOnReset?: boolean;
// Submit form on form changing
submitOnChange?: boolean;
// Col configuration for the entire form // Col configuration for the entire form
labelCol?: Partial<ColEx>; labelCol?: Partial<ColEx>;
// Col configuration for the entire form // Col configuration for the entire form
@ -129,7 +132,7 @@ export interface FormSchema {
// Variable name bound to v-model Default value // Variable name bound to v-model Default value
valueField?: string; valueField?: string;
// Label name // Label name
label: string; label: string | VNode;
// Auxiliary text // Auxiliary text
subLabel?: string; subLabel?: string;
// Help text on the right side of the text // Help text on the right side of the text
@ -148,11 +151,11 @@ export interface FormSchema {
// Component parameters // Component parameters
componentProps?: componentProps?:
| ((opt: { | ((opt: {
schema: FormSchema; schema: FormSchema;
tableAction: TableActionType; tableAction: TableActionType;
formActionType: FormActionType; formActionType: FormActionType;
formModel: Recordable; formModel: Recordable;
}) => Recordable) }) => Recordable)
| object; | object;
// Required // Required
required?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean); required?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);

6
src/components/Form/src/types/index.ts

@ -91,12 +91,15 @@ export type ComponentType =
| 'Select' | 'Select'
| 'ApiSelect' | 'ApiSelect'
| 'TreeSelect' | 'TreeSelect'
| 'ApiTree'
| 'ApiTreeSelect' | 'ApiTreeSelect'
| 'ApiRadioGroup'
| 'RadioButtonGroup' | 'RadioButtonGroup'
| 'RadioGroup' | 'RadioGroup'
| 'Checkbox' | 'Checkbox'
| 'CheckboxGroup' | 'CheckboxGroup'
| 'AutoComplete' | 'AutoComplete'
| 'ApiCascader'
| 'Cascader' | 'Cascader'
| 'DatePicker' | 'DatePicker'
| 'MonthPicker' | 'MonthPicker'
@ -109,4 +112,5 @@ export type ComponentType =
| 'IconPicker' | 'IconPicker'
| 'Render' | 'Render'
| 'Slider' | 'Slider'
| 'Rate'; | 'Rate'
| 'Divider';

5
src/main.ts

@ -29,7 +29,6 @@ import {
Export, Export,
Keyboard, Keyboard,
Validator, Validator,
Header,
Footer, Footer,
// 可选组件 // 可选组件
@ -102,6 +101,7 @@ async function bootstrap() {
app.mount('#app', true); app.mount('#app', true);
// 高德地图安全配置 // 高德地图安全配置
// @ts-ignore
window._AMapSecurityConfig = { window._AMapSecurityConfig = {
securityJsCode: 'ea9d5e2fb6383665de6c3c7b4e53c289' securityJsCode: 'ea9d5e2fb6383665de6c3c7b4e53c289'
}; };
@ -113,8 +113,7 @@ async function bootstrap() {
}); });
// 表格功能 // 表格功能
app.use(Header) app.use(Footer)
.use(Footer)
.use(Icon) .use(Icon)
.use(Filter) .use(Filter)
.use(Edit) .use(Edit)

57
src/views/common/boxcard/BoxCardModal.vue

@ -1,57 +0,0 @@
<template>
<BasicModal v-bind="$attrs"
:title="getTitle"
@register="registerModal"
@ok="handleSubmit"
>
<BasicForm @register="registerForm"/>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {cardFormSchema} from './boxcard.data';
import {BasicForm, useForm} from '/@/components/Form';
import {set} from '/@/api/platform/common/controller/boxcard';
const isUpdate = ref(true);
// emit
const emit = defineEmits(['success', 'register']);
/**
* 表单
*/
const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
labelWidth: 100,
schemas: cardFormSchema,
showActionButtonGroup: false,
actionColOptions: {
span: 23,
},
});
/**
* 表单参数
*/
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
resetFields();
setModalProps({confirmLoading: false});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
setFieldsValue({
...data.record,
});
}
});
//
const getTitle = computed(() => (!unref(isUpdate) ? '新增网卡' : '编辑网卡'));
async function handleSubmit() {
try {
const values = await validate();
setModalProps({confirmLoading: true});
await set(values);//
closeModal();
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>

160
src/views/common/boxcard/boxcard.data.ts

@ -1,160 +0,0 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import {h} from 'vue';
import {Tag} from 'ant-design-vue';
export const columns: BasicColumn[] = [
{
title: 'ID',
dataIndex: 'id',
width: 120,
},
{
title: 'iccid',
dataIndex: 'iccid',
width: 120,
},
{
title: '卡号',
dataIndex: 'card',
width: 120,
},
{
title: '网卡厂商',
dataIndex: 'company',
width: 120,
customRender: ({ record }) => {
const com = record.company;
switch (com) {
case '1': return '中国移动';
case '2': return '中国联通';
case '3': return '中国电信';
default: return '未知厂商';
}
}
},
{
title: '卡是否已使用',
dataIndex: 'isUsed',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
customRender: ({ record }) => {
const status = record.status;
const enable = status === '0';
const color = enable ? 'green' : 'red';
const text = enable ? '启用' : '禁用';
return h(Tag, { color: color }, () => text);
},
},
{
title: '创建人',
dataIndex: 'createByName',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
}
];
export const searchFormSchema: FormSchema[] = [
{
field: 'iccid',
label: 'iccid',
component: 'Input',
componentProps: {
placeholder: '请输入唯一标识iccid',
},
colProps: { span: 5 },
},
{
field: 'card',
label: '卡号',
component: 'Input',
componentProps: {
placeholder: '请输入卡号',
},
colProps: { span: 5 },
},
{
field: 'company',
label: '网卡厂商',
component: 'Input',
componentProps: {
placeholder: '请输入网卡厂商',
},
colProps: { span: 4 },
},
{
field: 'beginTime',
label: '起始时间',
component: 'DatePicker',
colProps: { span: 5 },
},
{
field: 'endTime',
label: '截止时间',
component: 'DatePicker',
colProps: { span: 5 },
},
];
export const cardFormSchema: FormSchema[] = [
{
field: 'id',
label: 'ID',
component: 'Input',
show: false,
},
{
field: 'iccid',
label: 'iccid',
component: 'Input',
helpMessage: ['唯一识别号'],
required: true
},
{
field: 'card',
label: '卡号',
component: 'Input',
required: false,
},
{
field: 'company',
label: '网卡厂商',
component: 'Select',
required: true,
componentProps: {
options: [
{ label: '中国移动', value: '1' },
{ label: '中国联通', value: '2' },
{ label: '中国电信', value: '3' },
{ label: '未知厂商', value: ''||undefined||null }
],
}
},
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
},
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
}
];

94
src/views/common/boxcard/index.vue

@ -1,94 +0,0 @@
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleCreate">新增网卡</a-button>
</template>
<template #action="{ record }">
<TableAction
:actions="[
{
icon: 'clarity:note-edit-line',
onClick: handleEdit.bind(null, record),
},
{
icon: 'ant-design:delete-outlined',
color: 'error',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</BasicTable>
<BoxCardModal @register="registerModal" @success="handleSuccess"/>
</div>
</template>
<script lang="ts" setup>
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import {useModal} from '/@/components/Modal';
import BoxCardModal from './BoxCardModal.vue';
import {columns, searchFormSchema} from './boxcard.data';
import {useMessage} from '/@/hooks/web/useMessage';
import {cardList, remove} from '/@/api/platform/common/controller/boxcard';
const {createMessage} = useMessage();
const [registerModal, {openModal}] = useModal();
const [registerTable, {reload}] = useTable({
title: '网卡列表',
api: cardList,
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
pagination: true,
striped: true,
useSearchForm: true,
showTableSetting: true,
bordered: true,
showIndexColumn: true,
canResize: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
slots: {customRender: 'action'},
fixed: undefined,
},
});
/**
* 创建菜单
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑菜单
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 删除菜单
*/
async function handleDelete(record: Recordable) {
await remove({ids: record.id});
createMessage.success('删除成功!');
handleSuccess();
}
/**
* 成功后重载表格
*/
function handleSuccess() {
reload();
}
</script>

9
src/views/common/doctor/DoctorModal.vue

@ -16,10 +16,9 @@
*/ */
import { ref, unref } from 'vue'; import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form'; import { BasicForm, useForm } from '/@/components/Form';
import { doctorFormSchema } from './doctor.data'; import { formSchema } from './doctor.data';
import {addDoctor, delDoctor,getDoctor,editDoctor} from '/@/api/platform/common/controller/doctor'; import {addDoctor, delDoctor, getDoctor, editDoctor} from '/@/api/platform/common/controller/doctor';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal'; import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { isEmpty } from '/@/utils/is';
/** 通用变量统一声明区域 */ /** 通用变量统一声明区域 */
const tag = ref<Nullable<string>>(''); const tag = ref<Nullable<string>>('');
@ -27,7 +26,7 @@
const emit = defineEmits(['success', 'register']); const emit = defineEmits(['success', 'register']);
const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({ const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
labelWidth: 100, labelWidth: 100,
schemas: doctorFormSchema, schemas: formSchema,
showActionButtonGroup: false, showActionButtonGroup: false,
baseColProps: { span: 24 } baseColProps: { span: 24 }
}); });
@ -42,7 +41,7 @@
// tag // tag
switch (unref(tag)) { switch (unref(tag)) {
case 'add': case 'add':
props.title = '医生'; props.title = '新增医生';
break; break;
case 'edit': case 'edit':
props.title = '编辑医生'; props.title = '编辑医生';

473
src/views/common/doctor/doctor.data.ts

@ -1,274 +1,225 @@
import { BasicColumn } from '/@/components/Table'; import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table'; import { FormSchema } from '/@/components/Table';
import {h, watch, watchEffect, watchSyncEffect} from 'vue'; import { h } from 'vue';
import {Tag} from 'ant-design-vue'; import { Tag } from 'ant-design-vue';
import { list as hospitalList } from '/@/api/platform/common/controller/hospital'; import { listHospital } from '/@/api/platform/common/controller/hospital';
import {list as institutionList} from '/@/api/platform/common/controller/institution'; import { listOrg } from '/@/api/platform/common/controller/org';
import { listOffice as officeList } from '/@/api/platform/common/controller/office'; import { listOffice } from '/@/api/platform/common/controller/office';
import {deepMerge} from '/@/utils';
export const columns: BasicColumn[] = [ export const columns: BasicColumn[] = [
{ {
title: 'ID', title: 'ID',
dataIndex: 'id', dataIndex: 'id',
width: 120, width: 120,
}, },
{ {
title: '医生姓名', title: '医生姓名',
dataIndex: 'name', dataIndex: 'name',
width: 120, width: 120,
}, },
{ {
title: '医生职称', title: '医生职称',
dataIndex: 'title', dataIndex: 'title',
width: 120, width: 120,
}, },
{ {
title: '医生性别', title: '医生电话',
dataIndex: 'sex', dataIndex: 'phone',
width: 120, width: 120,
customRender: ({ record }) => { },
switch (record.sex) { {
case 'M': return '男'; title: '医生邮箱',
case 'F': return '女'; dataIndex: 'email',
default: return '未知'; width: 120,
} },
} {
}, title: '详细地址',
{ dataIndex: 'detailAddress',
title: '医生电话', width: 120,
dataIndex: 'phone', },
width: 120, {
}, title: '所属组织类型',
{ dataIndex: 'orgType',
title: '医生邮箱', width: 120,
dataIndex: 'email', customRender: ({ record }) => {
width: 120, const orgType = record.orgType;
}, let text = '', color = '';
{ switch (orgType) {
title: '地址', case '1':
dataIndex: 'detailAddress', text = '医院';
width: 120, color = 'green';
}, break;
{ case '2':
title: '所属组织类型', text = '医检';
dataIndex: 'organType', color = 'blue';
width: 120, break;
customRender: ({ record }) => { }
const organType = record.organType; return h(Tag, { color: color }, () => text);
let text = ''; }
let color = ''; },
switch (organType) { {
case '1': title: '状态',
text = '医院'; dataIndex: 'status',
color = 'green'; width: 100,
break; customRender: ({ record }) => {
case '2': const status = record.status;
text = '医检'; // 采用二进制~~取反,只要为null或者0等等一些其他的空元素都会转为0
color = 'blue'; // 第一次取反会运算为-1,在后一次取反会运算为0
break; const enable = ~~status === 0;
default: const color = enable ? 'green' : 'red';
text = '未知'; const text = enable ? '启用' : '禁用';
color = 'gray'; return h(Tag, { color: color }, () => text);
break;
}
return h(Tag, { color: color }, () => text);
}
},
// {
// title: '科室名称',
// dataIndex: 'officeName',
// width: 120,
// },
{
title: '状态',
dataIndex: 'status',
width: 100,
customRender: ({ record }) => {
const status = record.status;
const enable = status === '0';
const color = enable ? 'green' : 'red';
const text = enable ? '启用' : '禁用';
return h(Tag, { color: color }, () => text);
},
},
{
title: '创建人',
dataIndex: 'createByName',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
} }
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
}
]; ];
export const searchFormSchema: FormSchema[] = [ export const searchFormSchema: FormSchema[] = [
{ {
field: 'name', field: 'name',
label: '医生姓名', label: '医生姓名',
component: 'Input', component: 'Input',
componentProps: { componentProps: {
placeholder: '请输入医生姓名', placeholder: '请输入医生姓名',
}, },
colProps: { span: 5 }, colProps: { span: 8 },
}, },
{ {
field: 'organType', field: 'orgType',
label: '组织类型', label: '组织类型',
component: 'Select', component: 'Select',
componentProps: {
componentProps: { options: [
options: [ { label: '医院',value: '1' },
{label: '医院',value: '1'}, { label: '医检',value: '2' }
{label: '医检',value: '2'} ]
] },
}, colProps: { span: 7 }
colProps: {span: 5} },
}, {
{ field: 'dateRange',
field: 'dateRange', label: '创建时间',
label: '创建时间', component: 'RangePicker',
component: 'RangePicker', componentProps: {
componentProps: { style: { width:'100%' },
style: { width:'100%' }, valueFormat: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD', placeholder: ['开始日期','结束日期']
placeholder: ['开始日期','结束日期'] },
}, colProps: { span: 8 }
colProps: { span: 8 } }
}
]; ];
export const doctorFormSchema: FormSchema[] = [ export const formSchema: FormSchema[] = [
{ {
field: 'id', field: 'id',
label: 'ID', label: 'ID',
component: 'Input', component: 'Input',
show: false, show: false,
}, },
{ {
field: 'name', field: 'name',
label: '医生姓名', label: '医生姓名',
component: 'Input', component: 'Input',
required: true
},
{
field: 'title',
label: '医生职称',
component: 'Input',
required: true,
},
{
field: 'sex',
label: '医生性别',
component: 'Select',
defaultValue: '0',
componentProps: {
options: [
{ label: '男', value: '0' },
{ label: '女', value: '1' },
]
}
},
{
field: 'phone',
label: '医生电话',
component: 'Input',
rules: [
{
pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'),
message: '请输入正确的手机号',
validateTrigger: 'change',
required: true required: true
}, }
{ ],
field: 'title', },
label: '医生职称', {
component: 'Input', field: 'email',
required: true, label: '医生邮箱',
}, component: 'Input',
{ rules: [
field: 'sex', {
label: '医生性别', type: 'email',
component: 'Select', message: '请输入正确的邮箱',
required: false, validateTrigger: 'change',
defaultValue: 'M', required: true
componentProps: { }
options: [ ],
{ label: '男', value: 'M' }, },
{ label: '女', value: 'F' }, {
] field: 'detailAddress',
} label: '地址',
}, component: 'Input',
{ required: false,
field: 'phone', },
label: '医生电话', {
component: 'Input', field: 'orgType',
rules: [ label: '所属组织类型',
{ component: 'Select',
pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'), defaultValue: '1',
message: '请输入正确的手机号', componentProps: {
validateTrigger: 'change', options: [
required: true { label: '医院', value: '1' },
} { label: '医检', value: '2' }
], ]
}, },
{ required: true,
field: 'email', },
label: '医生邮箱', {
component: 'Input', field: 'orgId',
rules: [ label: '所属组织',
{ component: 'ApiSelect',
type: 'email', required: true,
message: '请输入正确的邮箱', renderComponentContent: ({ schema, values, model, field }) => {
validateTrigger: 'change', const organType = model.orgType;
required: true const dataApi = ~~organType === 1 ? listHospital : listOrg;
} schema.componentProps = {
], api: dataApi,
}, labelField: 'name',
{ valueField: 'id',
field: 'detailAddress', resultField: 'data'
label: '地址', };
component: 'Input',
required: false,
},
{
field: 'organType',
label: '所属组织类型',
component: 'Select',
componentProps: {
options: [
{ label: '医院', value: '1' },
{ label: '医检', value: '2' }
]
},
required: true,
},
// {
// field: 'organId',
// label: '所属组织',
// component: 'ApiSelect',
// required: true,
// renderComponentContent: ({ schema, values, model, field }) => {
// const organType = model.organType;
// watch(organType,
// () => {
// const dataApi = organType=='1' ? hospitalList : institutionList;
// console.log(schema);
// schema.componentProps = deepMerge({
// api: dataApi,
// labelField: 'name',
// valueField: 'id'
// });
// },
// {
// immediate: true,
// }
// );
// }
// },
// {
// field: 'officeId',
// label: '所属科室',
// component: 'ApiSelect',
// componentProps: {
// mode: 'multiple',
// resultField: 'list',
// labelField: 'name',
// valueField: 'id',
// api: officeList,
// },
// },
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
},
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
} }
},
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
},
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
}
]; ];

5
src/views/common/doctor/index.vue

@ -4,9 +4,8 @@
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<template #toolbar> <template #toolbar>
<a-button <a-button type="primary"
type="primary" @click="handleAdd()"
@click="handleAdd()"
>新增医生</a-button> >新增医生</a-button>
<a-button type="primary" <a-button type="primary"
:disabled="state.single" :disabled="state.single"

117
src/views/common/hospital/HospitalModal.vue

@ -8,72 +8,71 @@
</BasicModal> </BasicModal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
/** /**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致 * 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法 * 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved. * Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔 * author entfrm开发团队-王翔
*/ */
import { ref, unref } from 'vue'; import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form'; import { BasicForm, useForm } from '/@/components/Form';
import { hospitalFormSchema } from './hospital.data'; import { formSchema } from './hospital.data';
import {addHospital, delHospital,getHospital,editHospital} from '/@/api/platform/common/controller/hospital'; import { addHospital, getHospital, editHospital } from '/@/api/platform/common/controller/hospital';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal'; import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { isEmpty } from '/@/utils/is';
/** 通用变量统一声明区域 */ /** 通用变量统一声明区域 */
const tag = ref<Nullable<string>>(''); const tag = ref<Nullable<string>>('');
/** https://v3.cn.vuejs.org/api/options-data.html#emits */ /** https://v3.cn.vuejs.org/api/options-data.html#emits */
const emit = defineEmits(['success', 'register']); const emit = defineEmits(['success', 'register']);
const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({ const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
labelWidth: 100, labelWidth: 100,
schemas: hospitalFormSchema, schemas: formSchema,
showActionButtonGroup: false, showActionButtonGroup: false,
baseColProps: { span: 24 } baseColProps: { span: 24 }
}); });
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: WindowInnerData = { _tag: '' }) => { const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: WindowInnerData = { _tag: '' }) => {
// //
await resetFields(); await resetFields();
await clearValidate(); await clearValidate();
// //
tag.value = data._tag; tag.value = data._tag;
const id = data.record?.id; const id = data.record?.id;
const props: Partial<ModalProps> = { confirmLoading: false }; const props: Partial<ModalProps> = { confirmLoading: false };
// tag
switch (unref(tag)) {
case 'add':
props.title = '新增医院';
break;
case 'edit':
props.title = '编辑医院';
await setFieldsValue(await getHospital(id));
break;
}
// :
setModalProps(props);
});
/** 处理弹出框提交 */
async function handleSubmit() {
try {
//
const formData = await validate();
//
setModalProps({ confirmLoading: true });
// tag // tag
switch (unref(tag)) { switch (unref(tag)) {
case 'add': case 'add':
await addHospital(formData); props.title = '新增医院';
break; break;
case 'edit': case 'edit':
await editHospital(formData); props.title = '编辑医院';
await setFieldsValue(await getHospital(id));
break; break;
} }
// // :
closeModal(); setModalProps(props);
emit('success'); });
} finally {
setModalProps({ confirmLoading: false }); /** 处理弹出框提交 */
async function handleSubmit() {
try {
//
const formData = await validate();
//
setModalProps({ confirmLoading: true });
// tag
switch (unref(tag)) {
case 'add':
await addHospital(formData);
break;
case 'edit':
await editHospital(formData);
break;
}
//
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
} }
}
</script> </script>

462
src/views/common/hospital/hospital.data.ts

@ -1,262 +1,226 @@
import { BasicColumn } from '/@/components/Table'; import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table'; import { FormSchema } from '/@/components/Table';
import {h} from 'vue'; import { h } from 'vue';
import {Tag} from 'ant-design-vue'; import { Tag } from 'ant-design-vue';
import {list as institutionList} from '/@/api/platform/common/controller/institution'; import { listOrg } from '/@/api/platform/common/controller/org';
export const columns: BasicColumn[] = [ export const columns: BasicColumn[] = [
{ {
title: 'ID', title: 'ID',
dataIndex: 'id', dataIndex: 'id',
width: 120, width: 120,
}, },
{ {
title: '医院名', title: '医院名',
dataIndex: 'name', dataIndex: 'name',
width: 120, width: 120,
}, },
{ {
title: '医院类型', title: '医院类型',
dataIndex: 'card', dataIndex: 'type',
width: 120, width: 120,
customRender: ({ record }) =>{ customRender: ({ record }) => {
const type = record.type; const type = record.type;
let text = ''; let text = '', color = '';
let color = ''; switch (type) {
switch (type) { case '0':
case '1': text = '其他医院';
text = '合作医院'; color = 'gray';
color = 'green'; break;
break; case '1':
case '0': text = '合作医院';
text = '其他医院'; color = 'green';
color = 'gray'; break;
break; }
default: return h(Tag, { color: color }, () => text);
text = '其他医院'; }
color = 'gray'; },
break; {
} title: '联系人',
dataIndex: 'contactName',
return h(Tag, { color: color }, () => text); width: 120,
} },
}, {
{ title: '联系人电话',
title: '联系人', dataIndex: 'contactPhone',
dataIndex: 'contactsName', width: 120,
width: 120, },
}, {
{ title: '联系人职称',
title: '联系人电话', dataIndex: 'contactTitle',
dataIndex: 'contactsTel', width: 120,
width: 120, },
}, {
{ title: '支付方式',
title: '联系人职称', dataIndex: 'payment',
dataIndex: 'contactsTitle', width: 120,
width: 120, customRender: ({ record }) => {
}, const com = record.payment;
{ switch (com) {
title: '支付方式', case '0': return '月付';
dataIndex: 'payment', case '1': return '预约付款';
width: 120, case '2': return '采样付款';
customRender: ({ record }) => { }
const com = record.payment;
switch (com) {
case '0': return '月付';
case '1': return '预约付款';
case '2': return '采样付款';
default: return '月付';
}
}
},
{
title: '详细地址',
dataIndex: 'detailAddress',
width: 180,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
customRender: ({ record }) => {
const status = record.status;
const enable = status === '0';
const color = enable ? 'green' : 'red';
const text = enable ? '启用' : '禁用';
return h(Tag, { color: color }, () => text);
},
},
{
title: '创建人',
dataIndex: 'createByName',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
} }
},
{
title: '详细地址',
dataIndex: 'detailAddress',
width: 180,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
customRender: ({ record }) => {
const status = record.status;
const enable = ~~status === 0;
const color = enable ? 'green' : 'red';
const text = enable ? '启用' : '禁用';
return h(Tag, { color: color }, () => text);
}
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
}
]; ];
export const searchFormSchema: FormSchema[] = [ export const searchFormSchema: FormSchema[] = [
{ {
field: 'name', field: 'name',
label: '医院名称', label: '医院名称',
component: 'Input', component: 'Input',
componentProps: { componentProps: {
placeholder: '请输入医院名称', placeholder: '请输入医院名称',
}, },
colProps: { span: 5 }, colProps: { span: 8 }
}, },
{ {
field: 'type', field: 'type',
label: '医院类型', label: '医院类型',
component: 'Select', component: 'Select',
componentProps: {
componentProps: { options: [
options: [ { label: '其他医院',value: '0' },
{label: '合作医院',value: '1'}, { label: '合作医院',value: '1' }
{label: '其他医院',value: '0'}, ]
] },
}, colProps: { span: 7 }
colProps: {span: 7} },
}, {
{ field: 'dateRange',
field: 'dateRange', label: '创建时间',
label: '创建时间', component: 'RangePicker',
component: 'RangePicker', componentProps: {
componentProps: { style: { width:'100%' },
style: { width:'100%' }, valueFormat: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD', placeholder: ['开始日期','结束日期']
placeholder: ['开始日期','结束日期'] },
}, colProps: { span: 8 }
colProps: { span: 8 } }
}
// {
// field: 'beginTime',
// label: '起始时间',
// component: 'DatePicker',
// colProps: { span: 5 },
// },
// {
// field: 'endTime',
// label: '截止时间',
// component: 'DatePicker',
// colProps: { span: 5 },
// },
]; ];
export const hospitalFormSchema: FormSchema[] = [ export const formSchema: FormSchema[] = [
{ {
field: 'id', field: 'id',
label: 'ID', label: 'ID',
component: 'Input', component: 'Input',
show: false, show: false,
}, },
{ {
field: 'name', field: 'name',
label: '医院名称', label: '医院名称',
component: 'Input', component: 'Input',
required: true required: true
}, },
{ {
field: 'type', field: 'type',
label: '医院类型', label: '医院类型',
component: 'Select', component: 'Select',
defaultValue: '0', defaultValue: '0',
componentProps: { componentProps: {
options: [ options: [
{label: '合作医院',value: '1'}, { label: '其他医院',value: '0' },
{label: '其他医院',value: '0'}, { label: '合作医院',value: '1' },
] ]
}, },
required: true, required: true,
}, },
{ {
field: 'contactsName', field: 'contactName',
label: '联系人名称', label: '联系人名称',
component: 'Input', component: 'Input',
required: true required: true
}, },
{ {
field: 'contactsTel', field: 'contactPhone',
label: '联系人电话', label: '联系人电话',
component: 'Input', component: 'Input',
rules: [ rules: [
{ {
pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'), pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'),
message: '请输入正确的手机号', message: '请输入正确的手机号',
validateTrigger: 'change', validateTrigger: 'change',
required: true
}
],
},
{
field: 'contactsTitle',
label: '联系人职称',
component: 'Input',
required: true
},
{
field: 'payment',
label: '支付方式',
component: 'Select',
required: true,
defaultValue: '0',
componentProps: {
options: [
{ label: '月付', value: '0' },
{ label: '预约付款', value: '1' },
{ label: '采样付款', value: '2' },
],
}
},
/* {
field: 'addressIds',
label: '地址',
component: 'ApiAddressCascader',
componentProps: {
api: addressList,
}
},*/
{
field: 'detailAddress',
label: '详细地址',
component: 'Input',
required: true required: true
}, }
{ ],
field: 'status', },
label: '状态', {
component: 'RadioButtonGroup', field: 'contactTitle',
defaultValue: '0', label: '联系人职称',
componentProps: { component: 'Input',
options: [ required: true
{ label: '启用', value: '0' }, },
{ label: '禁用', value: '1' }, {
], field: 'payment',
}, label: '支付方式',
}, component: 'Select',
{ required: true,
field: 'institutionIds', defaultValue: '0',
label: '关联机构', componentProps: {
component: 'ApiSelect', options: [
componentProps: { { label: '月付', value: '0' },
mode: 'multiple', { label: '预约付款', value: '1' },
resultField: 'list', { label: '采样付款', value: '2' },
labelField: 'name', ],
valueField: 'id',
api: institutionList,
},
required:true
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
} }
},
{
field: 'detailAddress',
label: '详细地址',
component: 'Input',
required: true
},
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
},
},
{
field: 'orgIds',
label: '关联机构',
component: 'ApiSelect',
componentProps: {
mode: 'multiple',
resultField: 'data',
labelField: 'name',
valueField: 'id',
api: listOrg
},
required:true
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea'
}
]; ];

174
src/views/common/hospital/index.vue

@ -39,98 +39,98 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
/** /**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致 * 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法 * 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved. * Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔 * author entfrm开发团队-王翔
*/ */
import { reactive, toRaw } from 'vue'; import { reactive, toRaw } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table'; import { BasicTable, useTable, TableAction } from '/@/components/Table';
import {list, delHospital} from '/@/api/platform/common/controller/hospital'; import { listHospital, delHospital } from '/@/api/platform/common/controller/hospital';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { columns, searchFormSchema } from './hospital.data'; import { columns, searchFormSchema } from './hospital.data';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import HospitalModal from './HospitalModal.vue'; import HospitalModal from './HospitalModal.vue';
/** 类型规范统一声明定义区域 */ /** 类型规范统一声明定义区域 */
interface TableState { interface TableState {
single: boolean; single: boolean;
multiple: boolean; multiple: boolean;
} }
/** 通用变量统一声明区域 */ /** 通用变量统一声明区域 */
const state = reactive<TableState>({ const state = reactive<TableState>({
// //
single: true, single: true,
// //
multiple: true multiple: true
}); });
const { createConfirm, createMessage } = useMessage(); const { createConfirm, createMessage } = useMessage();
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, clearSelectedRowKeys, getSelectRowKeys }] = useTable({ const [registerTable, { reload, clearSelectedRowKeys, getSelectRowKeys }] = useTable({
title: '医院', title: '医院',
api: list, api: listHospital,
rowKey: 'id', rowKey: 'id',
columns, columns,
formConfig: { formConfig: {
labelWidth: 120, labelWidth: 120,
schemas: searchFormSchema, schemas: searchFormSchema,
autoSubmitOnEnter: true, autoSubmitOnEnter: true,
fieldMapToTime: [['dateRange', ['beginTime', 'endTime'], 'YYYY-MM-DD']] fieldMapToTime: [['dateRange', ['beginTime', 'endTime'], 'YYYY-MM-DD']]
}, },
rowSelection: { type: 'checkbox' }, rowSelection: { type: 'checkbox' },
useSearchForm: true, useSearchForm: true,
showTableSetting: true, showTableSetting: true,
bordered: true, bordered: true,
clickToRowSelect: false, clickToRowSelect: false,
showIndexColumn: false, showIndexColumn: false,
actionColumn: { actionColumn: {
width: 220, width: 220,
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
slots: { customRender: 'action' }, slots: { customRender: 'action' },
fixed: false fixed: false
}, },
handleSearchInfoFn: () => clearSelectedRowKeys() handleSearchInfoFn: () => clearSelectedRowKeys()
}); });
/** 处理多选框选中数据 */ /** 处理多选框选中数据 */
function handleSelectionChange(selection?: Recordable) { function handleSelectionChange(selection?: Recordable) {
const rowSelection = toRaw(selection?.keys) || []; const rowSelection = toRaw(selection?.keys) || [];
state.single = rowSelection.length != 1; state.single = rowSelection.length != 1;
state.multiple = !rowSelection.length; state.multiple = !rowSelection.length;
} }
/** 新增按钮操作,行内新增与工具栏局域新增通用 */ /** 新增按钮操作,行内新增与工具栏局域新增通用 */
function handleAdd() { function handleAdd() {
openModal(true,{ _tag: 'add' }); openModal(true,{ _tag: 'add' });
} }
/** 编辑按钮操作,行内编辑 */ /** 编辑按钮操作,行内编辑 */
function handleEdit(record?: Recordable) { function handleEdit(record?: Recordable) {
record = record || { id: getSelectRowKeys() }; record = record || { id: getSelectRowKeys() };
openModal(true, { _tag: 'edit', record }); openModal(true, { _tag: 'edit', record });
} }
/** 删除按钮操作,行内删除 */ /** 删除按钮操作,行内删除 */
async function handleDel(record?: Recordable) { async function handleDel(record?: Recordable) {
const id = record?.id || getSelectRowKeys(); const id = record?.id || getSelectRowKeys();
createConfirm({ createConfirm({
iconType: 'warning', iconType: 'warning',
title: '警告', title: '警告',
content: `是否确认删除医院编号为${id}医院吗?`, content: `是否确认删除医院编号为${id}医院吗?`,
onOk: async () => { onOk: async () => {
await delHospital(id); await delHospital(id);
createMessage.success('删除成功!'); createMessage.success('删除成功!');
handleRefreshTable(); handleRefreshTable();
} }
}); });
} }
/** 处理表格刷新 */ /** 处理表格刷新 */
function handleRefreshTable() { function handleRefreshTable() {
clearSelectedRowKeys(); clearSelectedRowKeys();
reload(); reload();
} }
</script> </script>

136
src/views/common/institution/index.vue

@ -1,136 +0,0 @@
<template>
<div>
<BasicTable @register="registerTable"
@selection-change="handleSelectionChange"
>
<template #toolbar>
<a-button
type="primary"
@click="handleAdd()"
>新增机构</a-button>
<a-button type="primary"
:disabled="state.single"
@click="handleEdit()"
>修改机构</a-button>
<a-button type="primary"
:disabled="state.multiple"
@click="handleDel()"
>删除机构</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="[
{
label: '编辑',
icon: 'fa6-regular:pen-to-square',
onClick: handleEdit.bind(null, record)
},
{
label: '删除',
icon: 'ant-design:delete-outlined',
color: 'error',
onClick: handleDel.bind(null, record)
}]"
/>
</template>
</BasicTable>
<!--弹出窗体区域-->
<InstitutionModal @register="registerModal" @success="handleRefreshTable"/>
</div>
</template>
<script lang="ts" setup>
/**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔
*/
import { reactive, toRaw } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import {list, delInstitution} from '/@/api/platform/common/controller/institution';
import { useModal } from '/@/components/Modal';
import { columns, searchFormSchema } from './institution.data';
import { useMessage } from '/@/hooks/web/useMessage';
import InstitutionModal from './InstitutionModal.vue';
/** 类型规范统一声明定义区域 */
interface TableState {
single: boolean;
multiple: boolean;
}
/** 通用变量统一声明区域 */
const state = reactive<TableState>({
//
single: true,
//
multiple: true
});
const { createConfirm, createMessage } = useMessage();
const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, clearSelectedRowKeys, getSelectRowKeys }] = useTable({
title: '医生列表',
api: list,
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
fieldMapToTime: [['dateRange', ['beginTime', 'endTime'], 'YYYY-MM-DD']]
},
rowSelection: { type: 'checkbox' },
useSearchForm: true,
showTableSetting: true,
bordered: true,
clickToRowSelect: false,
showIndexColumn: false,
actionColumn: {
width: 220,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: false
},
handleSearchInfoFn: () => clearSelectedRowKeys()
});
/** 处理多选框选中数据 */
function handleSelectionChange(selection?: Recordable) {
const rowSelection = toRaw(selection?.keys) || [];
state.single = rowSelection.length != 1;
state.multiple = !rowSelection.length;
}
/** 新增按钮操作,行内新增与工具栏局域新增通用 */
function handleAdd() {
openModal(true,{ _tag: 'add' });
}
/** 编辑按钮操作,行内编辑 */
function handleEdit(record?: Recordable) {
record = record || { id: getSelectRowKeys() };
openModal(true, { _tag: 'edit', record });
}
/** 删除按钮操作,行内删除 */
async function handleDel(record?: Recordable) {
const id = record?.id || getSelectRowKeys();
createConfirm({
iconType: 'warning',
title: '警告',
content: `是否确认删除机构编号为${id}机构吗?`,
onOk: async () => {
await delInstitution(id);
createMessage.success('删除成功!');
handleRefreshTable();
}
});
}
/** 处理表格刷新 */
function handleRefreshTable() {
clearSelectedRowKeys();
reload();
}
</script>

4
src/views/common/office/OfficeModal.vue

@ -16,7 +16,7 @@
*/ */
import { ref, unref } from 'vue'; import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form'; import { BasicForm, useForm } from '/@/components/Form';
import { officeFormSchema } from './office.data'; import { formSchema } from './office.data';
import {addOffice, delOffice,getOffice,editOffice} from '/@/api/platform/common/controller/office'; import {addOffice, delOffice,getOffice,editOffice} from '/@/api/platform/common/controller/office';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal'; import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { isEmpty } from '/@/utils/is'; import { isEmpty } from '/@/utils/is';
@ -27,7 +27,7 @@ const tag = ref<Nullable<string>>('');
const emit = defineEmits(['success', 'register']); const emit = defineEmits(['success', 'register']);
const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({ const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
labelWidth: 100, labelWidth: 100,
schemas: officeFormSchema, schemas: formSchema,
showActionButtonGroup: false, showActionButtonGroup: false,
baseColProps: { span: 24 } baseColProps: { span: 24 }
}); });

389
src/views/common/office/office.data.ts

@ -1,217 +1,196 @@
import { BasicColumn } from '/@/components/Table'; import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table'; import { FormSchema } from '/@/components/Table';
import {h, watch} from 'vue'; import { h } from 'vue';
import {Tag} from 'ant-design-vue'; import { Tag } from 'ant-design-vue';
import { list as hospitalList } from '/@/api/platform/common/controller/hospital'; import { listHospital } from '/@/api/platform/common/controller/hospital';
import { list as institutionList } from '/@/api/platform/common/controller/institution'; import { listOrg } from '/@/api/platform/common/controller/org';
import {deepMerge} from '/@/utils';
export const columns: BasicColumn[] = [ export const columns: BasicColumn[] = [
{ {
title: 'ID', title: 'ID',
dataIndex: 'id', dataIndex: 'id',
width: 120, width: 120
}, },
{ {
title: '科室名称', title: '科室名称',
dataIndex: 'name', dataIndex: 'name',
width: 120, width: 120
}, },
{ {
title: '所属组织类型', title: '所属组织类型',
dataIndex: 'organType', dataIndex: 'orgType',
width: 120, width: 120,
customRender: ({ record }) => { customRender: ({ record }) => {
const organType = record.organType; const organType = record.orgType;
let text = ''; let text = '', color = '';
let color = ''; switch (organType) {
switch (organType) { case '1':
case '1': text = '医院';
text = '医院'; color = 'green';
color = 'green'; break;
break; case '2':
case '2': text = '医检';
text = '医检'; color = 'blue';
color = 'blue'; break;
break; }
default: return h(Tag, { color: color }, () => text);
text = '未知';
color = 'gray';
break;
}
return h(Tag, { color: color }, () => text);
}
},
// {
// title: '组织名称',
// dataIndex: 'organName',
// width: 120,
// },
{
title: '主任名称',
dataIndex: 'directorName',
width: 120,
},
{
title: '主任电话',
dataIndex: 'directorTel',
width: 120,
},
{
title: '地址',
dataIndex: 'detailAddress',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
customRender: ({ record }) => {
const status = record.status;
const enable = status === '0';
const color = enable ? 'green' : 'red';
const text = enable ? '启用' : '禁用';
return h(Tag, { color: color }, () => text);
},
},
{
title: '创建人',
dataIndex: 'createByName',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
} }
},
{
title: '组织名称',
dataIndex: 'orgName',
width: 120,
},
{
title: '主任名称',
dataIndex: 'manageName',
width: 120,
},
{
title: '主任电话',
dataIndex: 'managePhone',
width: 120,
},
{
title: '地址',
dataIndex: 'detailAddress',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
customRender: ({ record }) => {
const status = record.status;
const enable = ~~status === 0;
const color = enable ? 'green' : 'red';
const text = enable ? '启用' : '禁用';
return h(Tag, { color: color }, () => text);
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180
}
]; ];
export const searchFormSchema: FormSchema[] = [ export const searchFormSchema: FormSchema[] = [
{ {
field: 'name', field: 'name',
label: '科室名称', label: '科室名称',
component: 'Input', component: 'Input',
componentProps: { componentProps: {
placeholder: '请输入科室名称', placeholder: '请输入科室名称',
}, },
colProps: { span: 5 }, colProps: { span: 8 },
}, },
{ {
field: 'organType', field: 'orgType',
label: '组织类型', label: '组织类型',
component: 'Select', component: 'Select',
componentProps: {
componentProps: { options: [
options: [ {label: '医院',value: '1'},
{label: '医院',value: '1'}, {label: '医检',value: '2'}
{label: '医检',value: '2'} ]
] },
}, colProps: { span: 7 }
colProps: {span: 5} },
}, {
{ field: 'dateRange',
field: 'dateRange', label: '创建时间',
label: '创建时间', component: 'RangePicker',
component: 'RangePicker', componentProps: {
componentProps: { style: { width:'100%' },
style: { width:'100%' }, valueFormat: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD', placeholder: ['开始日期','结束日期']
placeholder: ['开始日期','结束日期'] },
}, colProps: { span: 8 }
colProps: { span: 8 } }
}
]; ];
export const officeFormSchema: FormSchema[] = [ export const formSchema: FormSchema[] = [
{ {
field: 'id', field: 'id',
label: 'ID', label: 'ID',
component: 'Input', component: 'Input',
show: false, show: false,
}, },
{ {
field: 'name', field: 'name',
label: '科室名称', label: '科室名称',
component: 'Input', component: 'Input',
required: true required: true
}, },
{ {
field: 'organType', field: 'orgType',
label: '所属组织类型', label: '所属组织类型',
component: 'Select', component: 'Select',
componentProps: { componentProps: {
options: [ options: [
{ label: '医院', value: '1' }, { label: '医院', value: '1' },
{ label: '医检', value: '2' } { label: '医检', value: '2' }
] ]
}, },
required: true, required: true,
}, },
// { {
// field: 'organId', field: 'orgId',
// label: '所属组织', label: '所属组织',
// component: 'ApiSelect', component: 'ApiSelect',
// required: true, required: true,
// renderComponentContent: ({ schema, values, model, field }) => { renderComponentContent: ({ schema, values, model, field }) => {
// const organType = model.organType; const organType = model.orgType;
// watch(organType, const dataApi = ~~organType === 1 ? listHospital : listOrg;
// () => { schema.componentProps = {
// const dataApi = organType=='1' ? hospitalList : institutionList; api: dataApi,
// console.log(schema); labelField: 'name',
// schema.componentProps = deepMerge({ valueField: 'id',
// api: dataApi, resultField: 'data'
// labelField: 'name', };
// valueField: 'id'
// });
// },
// {
// immediate: true,
// }
// );
// },
// },
{
field: 'directorTel',
label: '主任电话',
component: 'Input',
rules: [
{
pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'),
message: '请输入正确的手机号',
validateTrigger: 'change',
required: true
}
],
},
{
field: 'directorName',
label: '主任名称',
component: 'Input',
required: false
},
{
field: 'detailAddress',
label: '科室地址',
component: 'Input',
required: false
},
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
},
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
} }
},
{
field: 'manageName',
label: '主任名称',
component: 'Input',
required: false
},
{
field: 'managePhone',
label: '主任电话',
component: 'Input',
rules: [
{
pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'),
message: '请输入正确的手机号',
validateTrigger: 'change',
required: true
}
],
},
{
field: 'detailAddress',
label: '科室地址',
component: 'Input',
required: false
},
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
},
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
}
]; ];

10
src/views/common/institution/InstitutionModal.vue → src/views/common/org/OrgModal.vue

@ -16,8 +16,8 @@
*/ */
import { ref, unref } from 'vue'; import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form'; import { BasicForm, useForm } from '/@/components/Form';
import { institutionFormSchema } from './institution.data'; import { institutionFormSchema } from './org.data';
import {addInstitution, delInstitution,getInstitution,editInstitution} from '/@/api/platform/common/controller/institution'; import {addOrg, delOrg,getOrg,editOrg} from '/@/api/platform/common/controller/org';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal'; import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { isEmpty } from '/@/utils/is'; import { isEmpty } from '/@/utils/is';
@ -46,7 +46,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
break; break;
case 'edit': case 'edit':
props.title = '编辑机构'; props.title = '编辑机构';
await setFieldsValue(await getInstitution(id)); await setFieldsValue(await getOrg(id));
break; break;
} }
// : // :
@ -63,10 +63,10 @@ async function handleSubmit() {
// tag // tag
switch (unref(tag)) { switch (unref(tag)) {
case 'add': case 'add':
await addInstitution(formData); await addOrg(formData);
break; break;
case 'edit': case 'edit':
await editInstitution(formData); await editOrg(formData);
break; break;
} }
// //

136
src/views/common/org/index.vue

@ -0,0 +1,136 @@
<template>
<div>
<BasicTable @register="registerTable"
@selection-change="handleSelectionChange"
>
<template #toolbar>
<a-button
type="primary"
@click="handleAdd()"
>新增机构</a-button>
<a-button type="primary"
:disabled="state.single"
@click="handleEdit()"
>修改机构</a-button>
<a-button type="primary"
:disabled="state.multiple"
@click="handleDel()"
>删除机构</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="[
{
label: '编辑',
icon: 'fa6-regular:pen-to-square',
onClick: handleEdit.bind(null, record)
},
{
label: '删除',
icon: 'ant-design:delete-outlined',
color: 'error',
onClick: handleDel.bind(null, record)
}]"
/>
</template>
</BasicTable>
<!--弹出窗体区域-->
<InstitutionModal @register="registerModal" @success="handleRefreshTable"/>
</div>
</template>
<script lang="ts" setup>
/**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔
*/
import { reactive, toRaw } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import {listOrg, delOrg} from '/@/api/platform/common/controller/org';
import { useModal } from '/@/components/Modal';
import { columns, searchFormSchema } from './org.data';
import { useMessage } from '/@/hooks/web/useMessage';
import InstitutionModal from './OrgModal.vue';
/** 类型规范统一声明定义区域 */
interface TableState {
single: boolean;
multiple: boolean;
}
/** 通用变量统一声明区域 */
const state = reactive<TableState>({
//
single: true,
//
multiple: true
});
const { createConfirm, createMessage } = useMessage();
const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, clearSelectedRowKeys, getSelectRowKeys }] = useTable({
title: '医生列表',
api: listOrg,
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
fieldMapToTime: [['dateRange', ['beginTime', 'endTime'], 'YYYY-MM-DD']]
},
rowSelection: { type: 'checkbox' },
useSearchForm: true,
showTableSetting: true,
bordered: true,
clickToRowSelect: false,
showIndexColumn: false,
actionColumn: {
width: 220,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: false
},
handleSearchInfoFn: () => clearSelectedRowKeys()
});
/** 处理多选框选中数据 */
function handleSelectionChange(selection?: Recordable) {
const rowSelection = toRaw(selection?.keys) || [];
state.single = rowSelection.length != 1;
state.multiple = !rowSelection.length;
}
/** 新增按钮操作,行内新增与工具栏局域新增通用 */
function handleAdd() {
openModal(true,{ _tag: 'add' });
}
/** 编辑按钮操作,行内编辑 */
function handleEdit(record?: Recordable) {
record = record || { id: getSelectRowKeys() };
openModal(true, { _tag: 'edit', record });
}
/** 删除按钮操作,行内删除 */
async function handleDel(record?: Recordable) {
const id = record?.id || getSelectRowKeys();
createConfirm({
iconType: 'warning',
title: '警告',
content: `是否确认删除机构编号为${id}机构吗?`,
onOk: async () => {
await delOrg(id);
createMessage.success('删除成功!');
handleRefreshTable();
}
});
}
/** 处理表格刷新 */
function handleRefreshTable() {
clearSelectedRowKeys();
reload();
}
</script>

0
src/views/common/institution/institution.data.ts → src/views/common/org/org.data.ts

6
src/views/system/project/ProjectModal.vue → src/views/common/project/ProjectModal.vue

@ -15,10 +15,10 @@
* author entfrm开发团队-王翔 * author entfrm开发团队-王翔
*/ */
import { ref, unref } from 'vue'; import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index'; import { BasicForm, useForm } from '/@/components/Form';
import { formSchema } from './project.data'; import { formSchema } from './project.data';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal'; import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import {listProject, addProject, editProject, getProject} from '/@/api/platform/system/controller/project'; import {listProject, addProject, editProject, getProject} from '/@/api/platform/common/controller/project';
import { listToTree } from '/@/utils/helper/treeHelper'; import { listToTree } from '/@/utils/helper/treeHelper';
/** 通用变量统一声明区域 */ /** 通用变量统一声明区域 */
@ -86,4 +86,4 @@ async function handleSubmit() {
} }
} }
</script> </script>

4
src/views/system/project/index.vue → src/views/common/project/index.vue

@ -55,7 +55,7 @@ import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import DeptModal from './ProjectModal.vue'; import DeptModal from './ProjectModal.vue';
import { columns, searchFormSchema } from './project.data'; import { columns, searchFormSchema } from './project.data';
import {delProject, getProject, listProject} from '/@/api/platform/system/controller/project'; import {delProject, getProject, listProject} from '/@/api/platform/common/controller/project';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { listToTree } from '/@/utils/helper/treeHelper'; import { listToTree } from '/@/utils/helper/treeHelper';
@ -113,4 +113,4 @@ function handleRefreshTable() {
reload(); reload();
} }
</script> </script>

0
src/views/system/project/project.data.ts → src/views/common/project/project.data.ts

79
src/views/common/report/ReportModal.vue

@ -1,79 +0,0 @@
<template>
<BasicModal v-bind="$attrs"
width="720px"
@register="registerModal"
@ok="handleSubmit"
>
<BasicForm @register="registerForm"/>
</BasicModal>
</template>
<script lang="ts" setup>
/**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔
*/
import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form';
import { doctorFormSchema } from './report.data';
import {addReport, delReport,getReport,editReport} from '/@/api/platform/common/controller/report';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { isEmpty } from '/@/utils/is';
/** 通用变量统一声明区域 */
const tag = ref<Nullable<string>>('');
/** https://v3.cn.vuejs.org/api/options-data.html#emits */
const emit = defineEmits(['success', 'register']);
const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
labelWidth: 100,
schemas: doctorFormSchema,
showActionButtonGroup: false,
baseColProps: { span: 24 }
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: WindowInnerData = { _tag: '' }) => {
//
await resetFields();
await clearValidate();
//
tag.value = data._tag;
const id = data.record?.id;
const props: Partial<ModalProps> = { confirmLoading: false };
// tag
switch (unref(tag)) {
case 'add':
props.title = '医生';
break;
case 'edit':
props.title = '编辑医生';
await setFieldsValue(await getReport(id));
break;
}
// :
setModalProps(props);
});
/** 处理弹出框提交 */
async function handleSubmit() {
try {
//
const formData = await validate();
//
setModalProps({ confirmLoading: true });
// tag
switch (unref(tag)) {
case 'add':
await addReport(formData);
break;
case 'edit':
await editReport(formData);
break;
}
//
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

256
src/views/common/report/index.vue

@ -1,256 +0,0 @@
<template>
<div>
<BasicTable @register="registerTable"
@selection-change="handleSelectionChange"
>
<template #tableTitle>
<a-button
type="primary"
preIcon="ant-design:plus-outlined"
@click="handleAdd()"
>新增</a-button>
<a-button
preIcon="ant-design:plus-outlined"
type="primary"
@click="handleAdd()"
>批量新增</a-button>
<a-button type="primary"
preIcon="ant-design:upload-outlined"
@click="handleClickMergeUpload()"
>合并上传</a-button>
<a-button type="primary"
preIcon="ant-design:user-outlined"
@click="handleClickBatchCollect()"
>设置收样员</a-button>
<a-button type="primary"
preIcon="ant-design:printer-outlined"
@click="handleClickBatchPrint()"
>批量打印</a-button>
<a-button type="primary"
preIcon="ant-design:download-outlined"
@click="handleClickBatchDownload()"
>批量下载</a-button>
<a-button type="primary"
preIcon="ant-design:export-outlined"
@click="handleClickExport(0)"
>导出</a-button>
<a-button preIcon="ant-design:export-outlined"
type="primary"
@click="handleClickExport(1)"
>合并导出</a-button>
<a-button type="primary"
preIcon="ant-design:export-outlined"
@click="handleClickExportPreCode()"
>导出预制码</a-button>
</template>
<template #action="{ record }">
<TableAction :dropDownActions="getDropDownAction(record)"/>
</template>
</BasicTable>
<!--弹出窗体区域-->
<ReportModal @register="registerModal" @success="handleRefreshTable"/>
</div>
</template>
<script lang="ts" setup>
/**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔
*/
import { reactive, ref, toRaw } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import {listReport, delReport} from '/@/api/platform/common/controller/report';
import { useModal } from '/@/components/Modal';
import { columns, searchFormSchema } from './report.data';
import { useMessage } from '/@/hooks/web/useMessage';
import ReportModal from './ReportModal.vue';
import { useDrawer } from '/@/components/Drawer';
const showFooter = ref(true);
/** 类型规范统一声明定义区域 */
interface TableState {
single: boolean;
multiple: boolean;
}
/** 通用变量统一声明区域 */
const state = reactive<TableState>({
//
single: true,
//
multiple: true
});
const { createConfirm, createMessage } = useMessage();
const [registerModal, { openModal }] = useModal();
const [rolePermissionDrawer, { openDrawer: openRolePermissionDrawer }] = useDrawer();
const [registerTable, { reload, clearSelectedRowKeys, getSelectRowKeys }] = useTable({
api: listReport,
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
fieldMapToTime: [['dateRange', ['beginTime', 'endTime'], 'YYYY-MM-DD']]
},
rowSelection: { type: 'checkbox' },
useSearchForm: true,
showTableSetting: true,
bordered: true,
clickToRowSelect: false,
showIndexColumn: false,
actionColumn: {
width: 220,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: false
},
handleSearchInfoFn: () => clearSelectedRowKeys()
});
/** 处理多选框选中数据 */
function handleSelectionChange(selection?: Recordable) {
const rowSelection = toRaw(selection?.keys) || [];
state.single = rowSelection.length != 1;
state.multiple = !rowSelection.length;
}
/** 新增按钮操作,行内新增与工具栏局域新增通用 */
function handleAdd() {
openModal(true,{ _tag: 'add' });
}
/** 编辑按钮操作,行内编辑 */
function handleEdit(record?: Recordable) {
record = record || { id: getSelectRowKeys() };
openModal(true, { _tag: 'edit', record });
}
/** 删除按钮操作,行内删除 */
async function handleDel(record?: Recordable) {
const id = record?.id || getSelectRowKeys();
createConfirm({
iconType: 'warning',
title: '警告',
content: `是否确认删除医生编号为${id}医生吗?`,
onOk: async () => {
await delReport(id);
createMessage.success('删除成功!');
handleRefreshTable();
}
});
}
/**编辑*/
function handleClickEditor(record) {
}
/**收取*/
function handleClickCollect(record) {
}
/**送达*/
function handleClickDelivered(record){
}
/**确认收到*/
function handleClickReceived(record){
}
/**上传结果*/
function handleClickUpload(record) {
}
/**查看病情*/
function handleClickPatientDetail(record){
}
/**上传病情*/
function handleClickHealthUpload(record) {
}
/**联系医生*/
function handleClickLinkDoctor(record) {
}
/**合并上传*/
function handleClickMergeUpload() {
}
/**设置收样员*/
function handleClickBatchCollect() {
}
/**批量打印*/
function handleClickBatchPrint() {
}
/**批量下载*/
function handleClickBatchDownload() {
}
/**导出*/
function handleClickExport() {
}
/**导出预制码*/
function handleClickExportPreCode() {
}
function getDropDownAction(record) {
return [
{
label: '查看',
onClick: handleEdit.bind(null, record),
},
{
label: '编辑',
onClick: handleClickEditor.bind(null, record),
},
{
label: '收取',
onClick: handleClickCollect.bind(null, record),
},
{
label: '送达',
onClick: handleClickDelivered.bind(null, record),
},
{
label: '确认收到',
onClick: handleClickReceived.bind(null, record),
},
{
label: '添加项目',
onClick: handleAdd.bind(null, record),
},
{
label: '上传结果',
onClick: handleClickUpload.bind(null, record),
},
{
label: '查看病情',
onClick: handleClickPatientDetail.bind(null, record),
},
{
label: '上传病情',
onClick: handleClickHealthUpload.bind(null, record),
},
{
label: '联系医生',
onClick: handleClickLinkDoctor.bind(null, record),
},
{
label: '删除',
onClick: handleDel.bind(null, record),
},
];
}
/** 处理表格刷新 */
function handleRefreshTable() {
clearSelectedRowKeys();
reload();
}
</script>

430
src/views/common/report/report.data.ts

@ -1,430 +0,0 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
export const columns: BasicColumn[] = [
{
title: '序号',
dataIndex: 'id',
width: 120,
},
{
title: '送检时间',
dataIndex: 'name',
width: 120,
},
{
title: '编号',
dataIndex: 'title',
width: 120,
},
{
title: '姓名-性别-年龄',
dataIndex: 'sex',
width: 120,
},
{
title: '医院-科室-医生',
dataIndex: 'phone',
width: 120,
},
{
title: '医检机构',
dataIndex: 'email',
width: 120,
},
{
title: '平台/医检条码号',
dataIndex: 'detailAddress',
width: 120,
},
{
title: '送检项目',
dataIndex: 'organType',
width: 120,
},
{
title: '检测/打印',
dataIndex: 'officeName',
width: 120,
},
{
title: '报告时间',
dataIndex: 'status',
width: 100,
},
{
title: '收样员',
dataIndex: 'createByName',
width: 180,
},
{
title: '创建人',
dataIndex: 'createTime',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
},
{
title: '上机状态',
dataIndex: 'createTime',
width: 180,
},
{
title: '最新打印时间',
dataIndex: 'createTime',
width: 180,
}
];
export const searchFormSchema: FormSchema[] = [
{
field: 'name',
label: ' ',
component: 'Input',
componentProps: {
placeholder: '平台/医检条码号',
},
colProps: { span: 5 },
},
{
field: 'organType',
label: ' ',
component: 'Input',
componentProps: {
placeholder: '送检项目',
},
colProps: {span: 5}
},
{
field: 'organType',
label: ' ',
component: 'Input',
componentProps: {
placeholder: '患者姓名/手机/身份证'
},
colProps: { span: 5 }
},
{
field: 'status',
label: ' ',
component: 'Select',
componentProps: {
placeholder: '收样员姓名',
},
colProps: {span: 5}
},
{
field: 'createByName',
label: ' ',
component: 'Select',
componentProps: {
placeholder: '医院名称',
},
colProps: {span: 5}
},
{
field: 'detailAddress',
label: ' ',
component: 'Select',
componentProps: {
placeholder: '科室名称',
},
colProps: {span: 5}
},
{
field: 'officeName',
label: ' ',
component: 'Select',
componentProps: {
placeholder: '医生名称',
},
colProps: {span: 5}
},
{
field: 'email',
label: ' ',
component: 'Select',
componentProps: {
placeholder: '医检名称',
},
colProps: {span: 5}
},
{
field: 'phone',
label: ' ',
component: 'Select',
componentProps: {
options:[
{label: '未收取',value:'0'},
{label: '已收取',value:'5'},
{label: '已送达',value:'10'},
{label: '已收到',value:'12'},
{label: '检测中',value:'15'},
{label: '已出部分结果',value:'17'},
{label: '已出结果',value:'20'},
],
placeholder: '报告单状态',
},
colProps: {span: 5}
},
{
field: 'sex',
label: ' ',
component: 'Select',
componentProps: {
options:[
{label: '检测中',value:'0'},
{label: '已出结果',value:'1'},
],
placeholder: '结果状态',
},
colProps: {span: 5}
},
{
field: 'card',
label: ' ',
component: 'Select',
componentProps: {
options:[
{label: '未打印',value:'0'},
{label: '已打印',value:'1'},
],
placeholder: '打印状态',
},
colProps: {span: 5}
},
{
field: 'dateRange',
label: ' ',
component: 'RangePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD',
placeholder: ['送检开始日期','送检结束日期']
},
colProps: { span: 5 }
},
{
field: 'contactsName',
label: ' ',
component: 'Select',
componentProps: {
options:[
{label: '条码号异常',value:'1'},
{label: '姓名/身份证异常',value:'2'},
{label: '年龄/性别异常',value: '3'},
{label: '多条结果',value: '4'},
{label: '报告单结果未全出',value: '7'},
{label: '认证错误',value: '8'}
],
placeholder: '异常状态',
},
colProps: {span: 5}
},
{
field: 'contactsTel',
label: ' ',
component: 'Input',
componentProps: {
placeholder: '采样编号',
},
colProps: {span: 5}
},
{
field: 'contactsTitle',
label: ' ',
component: 'Select',
defaultValue: '1',
componentProps: {
options:[
{label: '正常',value:'1'},
{label: '已删除',value:'-1'},
],
placeholder: '状态',
},
colProps: {span: 5}
},
];
export const doctorFormSchema: FormSchema[] = [
{
field: 'id',
label: '医院名称',
component: 'Select',
componentProps:{
},
required: true,
},
{
field: 'name',
label: '科室名称',
component: 'Select',
componentProps:{
},
required: true
},
{
field: 'title',
label: '医生名称',
component: 'Select',
componentProps:{
},
required: true,
},
{
field: 'title',
label: '项目名称',
component: 'Input',
required: true,
},
{
field: 'title',
label: '医院项目',
component: 'Select',
componentProps:{
},
required: true,
},
{
field: 'title',
label: '医检名称',
component: 'Select',
componentProps:{
},
required: true,
},
{
field: 'title',
label: '科室名称',
component: 'Select',
componentProps:{
},
required: true,
},
{
field: 'title',
label: '患者姓名',
component: 'Input',
required: true,
},
{
field: 'sex',
label: '患者性别',
component: 'Select',
required: false,
defaultValue: 'M',
componentProps: {
options: [
{ label: '男', value: 'M' },
{ label: '女', value: 'F' },
]
}
},
{
field: 'phone',
label: '医生电话',
component: 'Input',
rules: [
{
pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'),
message: '请输入正确的手机号',
validateTrigger: 'change',
required: true
}
],
},
{
field: 'email',
label: '医生邮箱',
component: 'Input',
rules: [
{
type: 'email',
message: '请输入正确的邮箱',
validateTrigger: 'change',
required: true
}
],
},
{
field: 'detailAddress',
label: '地址',
component: 'Input',
required: false,
},
{
field: 'organType',
label: '所属组织类型',
component: 'Select',
componentProps: {
options: [
{ label: '医院', value: '1' },
{ label: '医检', value: '2' }
]
},
required: true,
},
// {
// field: 'organId',
// label: '所属组织',
// component: 'ApiSelect',
// required: true,
// renderComponentContent: ({ schema, values, model, field }) => {
// const organType = model.organType;
// watch(organType,
// () => {
// const dataApi = organType=='1' ? hospitalList : institutionList;
// console.log(schema);
// schema.componentProps = deepMerge({
// api: dataApi,
// labelField: 'name',
// valueField: 'id'
// });
// },
// {
// immediate: true,
// }
// );
// }
// },
// {
// field: 'officeId',
// label: '所属科室',
// component: 'ApiSelect',
// componentProps: {
// mode: 'multiple',
// resultField: 'list',
// labelField: 'name',
// valueField: 'id',
// api: officeList,
// },
// },
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
},
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
}
];

1
tsconfig.json

@ -44,7 +44,6 @@
"types/**/*.ts", "types/**/*.ts",
"build/**/*.ts", "build/**/*.ts",
"build/**/*.d.ts", "build/**/*.d.ts",
"mock/**/*.ts",
"vite.config.ts", "vite.config.ts",
], ],
"exclude": ["node_modules", "dist", "**/*.js"] "exclude": ["node_modules", "dist", "**/*.js"]

Loading…
Cancel
Save