|
|
@@ -1,6 +1,26 @@
|
|
|
-import type { TransformList, TransformRecord } from '#/api';
|
|
|
-
|
|
|
-/** 岗位人员资质(接口就绪后替换为真实请求) */
|
|
|
+import type {
|
|
|
+ TransformBody,
|
|
|
+ TransformData,
|
|
|
+ TransformList,
|
|
|
+ TransformRecord,
|
|
|
+} from '#/api';
|
|
|
+
|
|
|
+import { http } from '#/api';
|
|
|
+import {
|
|
|
+ fromEmployee,
|
|
|
+ parseEmployeeListResponse,
|
|
|
+ syncEmployeeCertDict,
|
|
|
+ syncEmployeePostDict,
|
|
|
+ toEmployeePayload,
|
|
|
+ toEmployeeQuery,
|
|
|
+} from '#/api/model/personnel-qualification';
|
|
|
+import {
|
|
|
+ listDictByCodeMethod,
|
|
|
+ listCertificateNameDictMethod,
|
|
|
+ listPostTypeDictMethod,
|
|
|
+} from '#/api/method/dict';
|
|
|
+
|
|
|
+/** 岗位人员资质 */
|
|
|
export namespace PersonnelQualificationModel {
|
|
|
export type QualificationStatus = 'valid' | 'expiring' | 'expired';
|
|
|
|
|
|
@@ -66,14 +86,7 @@ export namespace PersonnelQualificationModel {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-export const POSITION_OPTIONS = [
|
|
|
- { label: '接方、审方', value: '接方、审方' },
|
|
|
- { label: '审方', value: '审方' },
|
|
|
- { label: '配药', value: '配药' },
|
|
|
- { label: '复核', value: '复核' },
|
|
|
- { label: '煎煮', value: '煎煮' },
|
|
|
- { label: '打包', value: '打包' },
|
|
|
-];
|
|
|
+export let POSITION_OPTIONS: Array<{ label: string; value: string }> = [];
|
|
|
|
|
|
export const CERTIFICATE_NAME_OPTIONS = [
|
|
|
{ label: '健康证', value: '健康证' },
|
|
|
@@ -105,543 +118,246 @@ export const QUALIFICATION_STATUS_LABELS: Record<
|
|
|
expired: '过期',
|
|
|
};
|
|
|
|
|
|
-/** 各证书类型独立的 mock 附件,避免多证共用同一套图片 */
|
|
|
-function getDefaultAttachments(
|
|
|
- certName: string,
|
|
|
- index: number,
|
|
|
-): PersonnelQualificationModel.CertificateAttachment[] {
|
|
|
- const attachmentSets: Record<
|
|
|
- string,
|
|
|
- PersonnelQualificationModel.CertificateAttachment[]
|
|
|
- > = {
|
|
|
- 健康证: [
|
|
|
- {
|
|
|
- id: `health-${index}-1`,
|
|
|
- name: '健康证正面',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-8.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- {
|
|
|
- id: `health-${index}-2`,
|
|
|
- name: '健康证反面',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-7.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- ],
|
|
|
- 中药保管员: [
|
|
|
- {
|
|
|
- id: `custodian-${index}-1`,
|
|
|
- name: '保管员资格证',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-6.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- {
|
|
|
- id: `custodian-${index}-2`,
|
|
|
- name: '保管员证书扫描件',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-5.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- ],
|
|
|
- 执业中药师: [
|
|
|
- {
|
|
|
- id: `pharmacist-${index}-1`,
|
|
|
- name: '执业药师资格证',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-4.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- {
|
|
|
- id: `pharmacist-${index}-2`,
|
|
|
- name: '执业药师注册证',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-3.jpeg',
|
|
|
- type: 'pdf',
|
|
|
- },
|
|
|
- ],
|
|
|
- 中药调剂员: [
|
|
|
- {
|
|
|
- id: `dispenser-${index}-1`,
|
|
|
- name: '调剂员资格证',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-2.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- {
|
|
|
- id: `dispenser-${index}-2`,
|
|
|
- name: '调剂员证书扫描件',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-1.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- ],
|
|
|
- };
|
|
|
-
|
|
|
- return (
|
|
|
- attachmentSets[certName] ?? [
|
|
|
- {
|
|
|
- id: `cert-${index}-1`,
|
|
|
- name: '证书附件',
|
|
|
- url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-8.jpeg',
|
|
|
- type: 'image',
|
|
|
- },
|
|
|
- ]
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-function buildCertificates(
|
|
|
- items: Array<
|
|
|
- Omit<PersonnelQualificationModel.Certificate, 'id' | 'attachments'> & {
|
|
|
- attachments?: PersonnelQualificationModel.CertificateAttachment[];
|
|
|
- }
|
|
|
- >,
|
|
|
-): PersonnelQualificationModel.Certificate[] {
|
|
|
- return items.map((item, index) => ({
|
|
|
- ...item,
|
|
|
- id: `cert-${index + 1}`,
|
|
|
- attachments: item.attachments ?? getDefaultAttachments(item.name, index),
|
|
|
- }));
|
|
|
-}
|
|
|
-
|
|
|
-function resolveWorstStatus(
|
|
|
- certificates: PersonnelQualificationModel.Certificate[],
|
|
|
-): PersonnelQualificationModel.QualificationStatus {
|
|
|
- if (certificates.some((item) => item.status === 'expired')) return 'expired';
|
|
|
- if (certificates.some((item) => item.status === 'expiring')) return 'expiring';
|
|
|
- return 'valid';
|
|
|
-}
|
|
|
-
|
|
|
-function buildPersonnel(
|
|
|
- data: Omit<
|
|
|
- PersonnelQualificationModel.Personnel,
|
|
|
- 'certificateNames' | 'qualificationStatus'
|
|
|
- >,
|
|
|
-): PersonnelQualificationModel.Personnel {
|
|
|
- const certificateNames = data.certificates.map((item) => item.name).join('、');
|
|
|
- return {
|
|
|
- ...data,
|
|
|
- certificateNames,
|
|
|
- qualificationStatus: resolveWorstStatus(data.certificates),
|
|
|
- };
|
|
|
-}
|
|
|
-
|
|
|
-const MOCK_PERSONNEL: PersonnelQualificationModel.Personnel[] = [
|
|
|
- buildPersonnel({
|
|
|
- id: '1',
|
|
|
- organizationType: 'enterprise',
|
|
|
- enterpriseId: 'e1',
|
|
|
- enterpriseName: '重药煎药中心',
|
|
|
- decoctionCenterId: 'c1',
|
|
|
- decoctionCenterName: '重药华东煎药中心2',
|
|
|
- name: '孙明1',
|
|
|
- positions: ['接方、审方'],
|
|
|
- employeeNo: '28473',
|
|
|
- idNumber: '330102199001012839',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '健康证',
|
|
|
- number: '944895756806594342',
|
|
|
- expiryDate: '2027-3-20',
|
|
|
- status: 'valid',
|
|
|
- type: '健康证',
|
|
|
- },
|
|
|
- {
|
|
|
- name: '中药保管员',
|
|
|
- number: '944895756806594343',
|
|
|
- expiryDate: '2026-4-15',
|
|
|
- status: 'expiring',
|
|
|
- type: '保管员',
|
|
|
- },
|
|
|
- {
|
|
|
- name: '执业中药师',
|
|
|
- number: '944895756806594344',
|
|
|
- expiryDate: '2028-6-30',
|
|
|
- status: 'valid',
|
|
|
- type: '执业药师',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '2',
|
|
|
- enterpriseId: 'e1',
|
|
|
- enterpriseName: '重药煎药中心',
|
|
|
- decoctionCenterId: 'c1',
|
|
|
- decoctionCenterName: '重药华东煎药中心2',
|
|
|
- name: '孙明2',
|
|
|
- positions: ['审方'],
|
|
|
- employeeNo: '28474',
|
|
|
- idNumber: '330103198802022840',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '健康证',
|
|
|
- number: '944895756806594345',
|
|
|
- expiryDate: '2025-1-10',
|
|
|
- status: 'expired',
|
|
|
- },
|
|
|
- {
|
|
|
- name: '执业中药师',
|
|
|
- number: '944895756806594346',
|
|
|
- expiryDate: '2027-8-20',
|
|
|
- status: 'valid',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '3',
|
|
|
- enterpriseId: 'e1',
|
|
|
- enterpriseName: '重药煎药中心',
|
|
|
- decoctionCenterId: 'c1',
|
|
|
- decoctionCenterName: '重药华东煎药中心2',
|
|
|
- name: '孙明3',
|
|
|
- positions: ['配药'],
|
|
|
- employeeNo: '28475',
|
|
|
- idNumber: '330104199203033841',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '中药调剂员',
|
|
|
- number: '944895756806594347',
|
|
|
- expiryDate: '2026-5-1',
|
|
|
- status: 'expiring',
|
|
|
- },
|
|
|
- {
|
|
|
- name: '健康证',
|
|
|
- number: '944895756806594348',
|
|
|
- expiryDate: '2027-2-18',
|
|
|
- status: 'valid',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '4',
|
|
|
- enterpriseId: 'e1',
|
|
|
- enterpriseName: '重药煎药中心',
|
|
|
- decoctionCenterId: 'c1',
|
|
|
- decoctionCenterName: '重药华东煎药中心2',
|
|
|
- name: '孙明4',
|
|
|
- positions: ['复核'],
|
|
|
- employeeNo: '28476',
|
|
|
- idNumber: '330105199504044842',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '健康证',
|
|
|
- number: '944895756806594349',
|
|
|
- expiryDate: '2027-12-31',
|
|
|
- status: 'valid',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '5',
|
|
|
- enterpriseId: 'e1',
|
|
|
- enterpriseName: '重药煎药中心',
|
|
|
- decoctionCenterId: 'c1',
|
|
|
- decoctionCenterName: '重药华东煎药中心2',
|
|
|
- name: '孙明5',
|
|
|
- positions: ['煎煮'],
|
|
|
- employeeNo: '28477',
|
|
|
- idNumber: '330106199605055843',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '健康证',
|
|
|
- number: '944895756806594350',
|
|
|
- expiryDate: '2024-12-1',
|
|
|
- status: 'expired',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '6',
|
|
|
- enterpriseId: 'e1',
|
|
|
- enterpriseName: '重药煎药中心',
|
|
|
- decoctionCenterId: 'c1',
|
|
|
- decoctionCenterName: '重药华东煎药中心2',
|
|
|
- name: '孙明6',
|
|
|
- positions: ['打包'],
|
|
|
- employeeNo: '28478',
|
|
|
- idNumber: '330107199706066844',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '健康证',
|
|
|
- number: '944895756806594351',
|
|
|
- expiryDate: '2027-9-10',
|
|
|
- status: 'valid',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '7',
|
|
|
- enterpriseId: 'e2',
|
|
|
- enterpriseName: '杭州中药煎配中心',
|
|
|
- decoctionCenterId: 'c2',
|
|
|
- decoctionCenterName: '西湖煎药中心',
|
|
|
- name: '李明1',
|
|
|
- positions: ['接方、审方'],
|
|
|
- employeeNo: '38473',
|
|
|
- idNumber: '330108199807077845',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '执业中药师',
|
|
|
- number: '944895756806594352',
|
|
|
- expiryDate: '2026-3-20',
|
|
|
- status: 'expiring',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '8',
|
|
|
- organizationType: 'enterprise',
|
|
|
- enterpriseId: 'e2',
|
|
|
- enterpriseName: '杭州中药煎配中心',
|
|
|
- decoctionCenterId: 'c2',
|
|
|
- decoctionCenterName: '西湖煎药中心',
|
|
|
- name: '李明2',
|
|
|
- positions: ['配药'],
|
|
|
- employeeNo: '38474',
|
|
|
- idNumber: '330109199908088846',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '中药调剂员',
|
|
|
- number: '944895756806594353',
|
|
|
- expiryDate: '2027-6-15',
|
|
|
- status: 'valid',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '9',
|
|
|
- organizationType: 'medicalInstitution',
|
|
|
- enterpriseId: 'm1',
|
|
|
- enterpriseName: '蒋村社区卫生服务中心',
|
|
|
- decoctionCenterId: 'c3',
|
|
|
- decoctionCenterName: '蒋村煎药中心',
|
|
|
- name: '王芳',
|
|
|
- positions: ['接方、审方'],
|
|
|
- employeeNo: '48473',
|
|
|
- idNumber: '330110199001011234',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '健康证',
|
|
|
- number: '944895756806594360',
|
|
|
- expiryDate: '2027-5-20',
|
|
|
- status: 'valid',
|
|
|
- },
|
|
|
- {
|
|
|
- name: '执业中药师',
|
|
|
- number: '944895756806594361',
|
|
|
- expiryDate: '2026-2-10',
|
|
|
- status: 'expiring',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
- buildPersonnel({
|
|
|
- id: '10',
|
|
|
- organizationType: 'medicalInstitution',
|
|
|
- enterpriseId: 'm2',
|
|
|
- enterpriseName: '西湖区中医院',
|
|
|
- decoctionCenterId: 'c4',
|
|
|
- decoctionCenterName: '西湖区中医院煎药室',
|
|
|
- name: '赵强',
|
|
|
- positions: ['配药'],
|
|
|
- employeeNo: '48474',
|
|
|
- idNumber: '330111198803022345',
|
|
|
- certificates: buildCertificates([
|
|
|
- {
|
|
|
- name: '中药调剂员',
|
|
|
- number: '944895756806594362',
|
|
|
- expiryDate: '2027-8-1',
|
|
|
- status: 'valid',
|
|
|
- },
|
|
|
- ]),
|
|
|
- }),
|
|
|
+const EMPLOYEE_BASE = '/manager/tcmp-pc/employee';
|
|
|
+const EMPLOYEE_UPLOAD = '/manager/tcmp-pc/external/employeeInfoUpload';
|
|
|
+const EMPLOYEE_CERT_DICT_CODES = [
|
|
|
+ 'certificate_name',
|
|
|
+ 'employee_cert',
|
|
|
+ 'cert_name',
|
|
|
+ 'qualification_name',
|
|
|
];
|
|
|
|
|
|
-let mockStore = [...MOCK_PERSONNEL];
|
|
|
+let postDictLoaded = false;
|
|
|
+let latestListSummary: PersonnelQualificationModel.ExpirySummary = {
|
|
|
+ expiredCount: 0,
|
|
|
+ expiringCount: 0,
|
|
|
+};
|
|
|
|
|
|
-function filterPersonnel(
|
|
|
- list: PersonnelQualificationModel.Personnel[],
|
|
|
- query?: PersonnelQualificationModel.ListQuery,
|
|
|
+function syncPostTypeOptions(
|
|
|
+ items: Array<{ dictName: string; dictValue: number | string }>,
|
|
|
) {
|
|
|
- if (!query) return list;
|
|
|
+ if (!items.length) return;
|
|
|
+ syncEmployeePostDict(items);
|
|
|
+ POSITION_OPTIONS.splice(
|
|
|
+ 0,
|
|
|
+ POSITION_OPTIONS.length,
|
|
|
+ ...items.map((item) => ({
|
|
|
+ label: item.dictName,
|
|
|
+ value: item.dictName,
|
|
|
+ })),
|
|
|
+ );
|
|
|
+}
|
|
|
|
|
|
- return list.filter((item) => {
|
|
|
- const organizationId = query.organizationId ?? query.enterpriseId;
|
|
|
- const organizationType = query.organizationType ?? 'enterprise';
|
|
|
+async function ensurePostDictLoaded() {
|
|
|
+ if (postDictLoaded) return;
|
|
|
|
|
|
- if (organizationId) {
|
|
|
- const itemType = item.organizationType ?? 'enterprise';
|
|
|
- if (itemType !== organizationType) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (item.enterpriseId !== organizationId) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- if (
|
|
|
- query.decoctionCenterId &&
|
|
|
- item.decoctionCenterId !== query.decoctionCenterId
|
|
|
- ) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (query.position && !item.positions.includes(query.position)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (
|
|
|
- query.qualificationStatus &&
|
|
|
- item.qualificationStatus !== query.qualificationStatus
|
|
|
- ) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (query.keyword) {
|
|
|
- const keyword = query.keyword.trim();
|
|
|
- if (
|
|
|
- !item.name.includes(keyword) &&
|
|
|
- !item.employeeNo.includes(keyword)
|
|
|
- ) {
|
|
|
- return false;
|
|
|
+ try {
|
|
|
+ const items = await listPostTypeDictMethod();
|
|
|
+ syncPostTypeOptions(items);
|
|
|
+ } catch {
|
|
|
+ // 字典未配置时岗位展示原始编码
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const items = await listCertificateNameDictMethod();
|
|
|
+ if (items.length) {
|
|
|
+ syncEmployeeCertDict(items);
|
|
|
+ } else {
|
|
|
+ for (const dictCode of EMPLOYEE_CERT_DICT_CODES.slice(1)) {
|
|
|
+ try {
|
|
|
+ const fallbackItems = await listDictByCodeMethod(dictCode);
|
|
|
+ if (fallbackItems.length) {
|
|
|
+ syncEmployeeCertDict(fallbackItems);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ // 字典未配置时证书名称展示原始编码
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- return true;
|
|
|
- });
|
|
|
-}
|
|
|
+ } catch {
|
|
|
+ // 字典未配置时证书名称展示原始编码
|
|
|
+ }
|
|
|
|
|
|
-function computeExpirySummary(): PersonnelQualificationModel.ExpirySummary {
|
|
|
- let expiredCount = 0;
|
|
|
- let expiringCount = 0;
|
|
|
+ postDictLoaded = true;
|
|
|
+}
|
|
|
|
|
|
- for (const person of mockStore) {
|
|
|
- if (person.qualificationStatus === 'expired') expiredCount += 1;
|
|
|
- else if (person.qualificationStatus === 'expiring') expiringCount += 1;
|
|
|
- }
|
|
|
+/** 岗位类型下拉选项(post_type) */
|
|
|
+export async function optionsPostTypeMethod() {
|
|
|
+ await ensurePostDictLoaded();
|
|
|
+ return [...POSITION_OPTIONS];
|
|
|
+}
|
|
|
|
|
|
- return { expiredCount, expiringCount };
|
|
|
+function toSelectOptions(
|
|
|
+ items: Array<{ label: string; value: string }>,
|
|
|
+): Array<{ label: string; value: string }> {
|
|
|
+ return items.filter((item) => item.label && item.value);
|
|
|
}
|
|
|
|
|
|
-/** 岗位人员资质列表(当前为本地 mock,后期对接后端接口) */
|
|
|
-export function listPersonnelQualificationsMethod(
|
|
|
+/** 岗位人员资质列表 */
|
|
|
+export async function listPersonnelQualificationsMethod(
|
|
|
page = 1,
|
|
|
size = 10,
|
|
|
query?: PersonnelQualificationModel.ListQuery,
|
|
|
-): Promise<TransformList<PersonnelQualificationModel.Personnel>> {
|
|
|
- const filtered = filterPersonnel(mockStore, query);
|
|
|
- const start = (page - 1) * size;
|
|
|
- const items = filtered.slice(start, start + size);
|
|
|
- return Promise.resolve({
|
|
|
- items,
|
|
|
- total: filtered.length,
|
|
|
- data: { page, size, total: filtered.length },
|
|
|
+) {
|
|
|
+ await ensurePostDictLoaded();
|
|
|
+
|
|
|
+ return http.get<
|
|
|
+ TransformList<PersonnelQualificationModel.Personnel> & {
|
|
|
+ summary: PersonnelQualificationModel.ExpirySummary;
|
|
|
+ },
|
|
|
+ TransformList
|
|
|
+ >(`${EMPLOYEE_BASE}/list`, {
|
|
|
+ params: toEmployeeQuery(page, size, query),
|
|
|
+ cacheFor: 0,
|
|
|
+ transform(data) {
|
|
|
+ const parsed = parseEmployeeListResponse(data);
|
|
|
+ const summary = {
|
|
|
+ expiredCount: parsed.expiredCount,
|
|
|
+ expiringCount: parsed.expiringCount,
|
|
|
+ };
|
|
|
+ latestListSummary = summary;
|
|
|
+ return {
|
|
|
+ total: parsed.total,
|
|
|
+ items: parsed.items.map((item) => fromEmployee(item)),
|
|
|
+ summary,
|
|
|
+ };
|
|
|
+ },
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-/** 资质到期统计(当前为本地 mock) */
|
|
|
+/** 资质到期统计(由列表接口同包返回,读取最近一次列表请求的统计) */
|
|
|
export function getPersonnelQualificationSummaryMethod(): Promise<PersonnelQualificationModel.ExpirySummary> {
|
|
|
- return Promise.resolve(computeExpirySummary());
|
|
|
+ return Promise.resolve(latestListSummary);
|
|
|
+}
|
|
|
+
|
|
|
+/** 岗位人员资质详情 */
|
|
|
+export async function getPersonnelQualificationMethod(id: string) {
|
|
|
+ await ensurePostDictLoaded();
|
|
|
+
|
|
|
+ return http.get<PersonnelQualificationModel.Personnel, TransformData>(
|
|
|
+ `${EMPLOYEE_BASE}/detailById`,
|
|
|
+ {
|
|
|
+ params: { id },
|
|
|
+ cacheFor: 0,
|
|
|
+ transform(data) {
|
|
|
+ return fromEmployee(data);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
-/** 岗位人员资质详情(当前为本地 mock) */
|
|
|
-export function getPersonnelQualificationMethod(id: string) {
|
|
|
- const item = mockStore.find((row) => row.id === id);
|
|
|
- if (!item) {
|
|
|
- return Promise.reject(new Error('人员记录不存在'));
|
|
|
+function parseEmployeeUploadResult(data: unknown, message?: string): string {
|
|
|
+ if (typeof data === 'string' && data.trim()) {
|
|
|
+ const value = data.trim();
|
|
|
+ if (/^https?:\/\//i.test(value) || value.startsWith('/')) return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data && typeof data === 'object') {
|
|
|
+ const record = data as TransformData;
|
|
|
+ const url =
|
|
|
+ record.url ?? record.fileUrl ?? record.filePath ?? record.path ?? '';
|
|
|
+ if (url) return String(url);
|
|
|
}
|
|
|
- return Promise.resolve({ ...item });
|
|
|
+
|
|
|
+ if (message?.startsWith('http')) return message;
|
|
|
+
|
|
|
+ throw new Error(message || '上传失败');
|
|
|
}
|
|
|
|
|
|
-/** 新增/修改岗位人员资质(当前为本地 mock) */
|
|
|
-export function editPersonnelQualificationMethod(
|
|
|
+/** 岗位人员资质附件上传 */
|
|
|
+export function uploadEmployeeQualificationAttachmentMethod(file: File) {
|
|
|
+ const data = new FormData();
|
|
|
+ data.append('file', file);
|
|
|
+ return http.post<string, TransformBody<unknown>>(EMPLOYEE_UPLOAD, data, {
|
|
|
+ meta: { notParseResponseBody: true },
|
|
|
+ transform(body: TransformBody<unknown>) {
|
|
|
+ if (body.code === 0) {
|
|
|
+ return parseEmployeeUploadResult(body.data, body.message);
|
|
|
+ }
|
|
|
+ // eslint-disable-next-line no-throw-literal
|
|
|
+ throw { message: body.message ?? '上传失败' };
|
|
|
+ },
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 新增/修改岗位人员资质 */
|
|
|
+export async function editPersonnelQualificationMethod(
|
|
|
data: PersonnelQualificationModel.PersonnelForm,
|
|
|
) {
|
|
|
- const certificates = data.certificates.map((cert, index) => ({
|
|
|
- ...cert,
|
|
|
- id: cert.id || `cert-${Date.now()}-${index}`,
|
|
|
- status: cert.status ?? 'valid',
|
|
|
- attachments: cert.attachments ?? [],
|
|
|
- }));
|
|
|
-
|
|
|
- const enterpriseName =
|
|
|
- mockStore.find((item) => item.decoctionCenterId === data.decoctionCenterId)
|
|
|
- ?.enterpriseName ?? '重药煎药中心';
|
|
|
- const decoctionCenterName =
|
|
|
- data.decoctionCenterName ??
|
|
|
- mockStore.find((item) => item.decoctionCenterId === data.decoctionCenterId)
|
|
|
- ?.decoctionCenterName ??
|
|
|
- '重药华东煎药中心2';
|
|
|
-
|
|
|
- const payload = buildPersonnel({
|
|
|
- id: data.id ?? String(Date.now()),
|
|
|
- enterpriseId: mockStore.find(
|
|
|
- (item) => item.decoctionCenterId === data.decoctionCenterId,
|
|
|
- )?.enterpriseId,
|
|
|
- enterpriseName,
|
|
|
- decoctionCenterId: data.decoctionCenterId,
|
|
|
- decoctionCenterName,
|
|
|
- name: data.name,
|
|
|
- positions: data.positions,
|
|
|
- employeeNo: data.employeeNo,
|
|
|
- idNumber: data.idNumber,
|
|
|
- certificates,
|
|
|
+ await ensurePostDictLoaded();
|
|
|
+
|
|
|
+ const payload = toEmployeePayload(data);
|
|
|
+ const url = data.id
|
|
|
+ ? `${EMPLOYEE_BASE}/updateInfo`
|
|
|
+ : `${EMPLOYEE_BASE}/saveInfo`;
|
|
|
+
|
|
|
+ const result = await http.post<TransformData, TransformData>(url, payload, {
|
|
|
+ cacheFor: 0,
|
|
|
});
|
|
|
|
|
|
- if (data.id) {
|
|
|
- const index = mockStore.findIndex((item) => item.id === data.id);
|
|
|
- if (index === -1) {
|
|
|
- return Promise.reject(new Error('人员记录不存在'));
|
|
|
- }
|
|
|
- mockStore[index] = payload;
|
|
|
- } else {
|
|
|
- mockStore = [payload, ...mockStore];
|
|
|
+ const savedId =
|
|
|
+ data.id ??
|
|
|
+ (result?.id === undefined || result?.id === null
|
|
|
+ ? undefined
|
|
|
+ : String(result.id));
|
|
|
+
|
|
|
+ if (savedId) {
|
|
|
+ return getPersonnelQualificationMethod(savedId);
|
|
|
}
|
|
|
|
|
|
- return Promise.resolve(payload);
|
|
|
+ return fromEmployee(result ?? payload);
|
|
|
}
|
|
|
|
|
|
-/** 删除岗位人员资质(当前为本地 mock) */
|
|
|
-export function deletePersonnelQualificationMethod(id: string) {
|
|
|
- const index = mockStore.findIndex((item) => item.id === id);
|
|
|
- if (index === -1) {
|
|
|
- return Promise.reject(new Error('人员记录不存在'));
|
|
|
- }
|
|
|
- mockStore.splice(index, 1);
|
|
|
- return Promise.resolve(true);
|
|
|
+/** 删除岗位人员资质(接口文档未提供,保留占位) */
|
|
|
+export function deletePersonnelQualificationMethod(_id: string) {
|
|
|
+ return Promise.reject(new Error('后端暂未提供删除接口'));
|
|
|
}
|
|
|
|
|
|
-/** 煎药企业选项(mock) */
|
|
|
-export function optionsPersonnelEnterpriseMethod() {
|
|
|
- const map = new Map<string, string>();
|
|
|
- for (const item of mockStore) {
|
|
|
- if ((item.organizationType ?? 'enterprise') !== 'enterprise') continue;
|
|
|
- if (item.enterpriseId) {
|
|
|
- map.set(item.enterpriseId, item.enterpriseName);
|
|
|
- }
|
|
|
- }
|
|
|
- return Promise.resolve(
|
|
|
- [...map.entries()].map(([value, label]) => ({ label, value })),
|
|
|
+/** 煎药企业选项 */
|
|
|
+export async function optionsPersonnelEnterpriseMethod() {
|
|
|
+ const { listDecoctionCentersAllMethod } = await import('#/api/method/system');
|
|
|
+ const items = await listDecoctionCentersAllMethod();
|
|
|
+ return toSelectOptions(
|
|
|
+ items.map((item) => ({
|
|
|
+ label: item.name,
|
|
|
+ value: item.code || item.id,
|
|
|
+ })),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
-/** 医疗机构选项(mock) */
|
|
|
-export function optionsPersonnelMedicalInstitutionMethod() {
|
|
|
- const map = new Map<string, string>();
|
|
|
- for (const item of mockStore) {
|
|
|
- if (item.organizationType !== 'medicalInstitution') continue;
|
|
|
- if (item.enterpriseId) {
|
|
|
- map.set(item.enterpriseId, item.enterpriseName);
|
|
|
- }
|
|
|
- }
|
|
|
- return Promise.resolve(
|
|
|
- [...map.entries()].map(([value, label]) => ({ label, value })),
|
|
|
+/** 医疗机构选项 */
|
|
|
+export async function optionsPersonnelMedicalInstitutionMethod() {
|
|
|
+ const { listMedicalInstitutionsMethod } = await import('#/api/method/system');
|
|
|
+ const items = await listMedicalInstitutionsMethod();
|
|
|
+ return toSelectOptions(
|
|
|
+ items.map((item) => ({
|
|
|
+ label: item.name,
|
|
|
+ value: item.code || item.id,
|
|
|
+ })),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
-/** 煎药中心选项(mock) */
|
|
|
-export function optionsPersonnelDecoctionCenterMethod(
|
|
|
+/** 煎药中心选项 */
|
|
|
+export async function optionsPersonnelDecoctionCenterMethod(
|
|
|
organizationId?: string,
|
|
|
organizationType: PersonnelQualificationModel.OrganizationType = 'enterprise',
|
|
|
) {
|
|
|
- const map = new Map<string, string>();
|
|
|
- for (const item of mockStore) {
|
|
|
- const itemType = item.organizationType ?? 'enterprise';
|
|
|
- if (itemType !== organizationType) continue;
|
|
|
- if (organizationId && item.enterpriseId !== organizationId) continue;
|
|
|
- if (item.decoctionCenterId) {
|
|
|
- map.set(item.decoctionCenterId, item.decoctionCenterName);
|
|
|
+ const { listMedicineCentersMethod } = await import('#/api/method/system');
|
|
|
+ const query: TransformData = {};
|
|
|
+ if (organizationId) {
|
|
|
+ if (organizationType === 'medicalInstitution') {
|
|
|
+ query.institutionId = organizationId;
|
|
|
+ } else {
|
|
|
+ query.enterpriseId = organizationId;
|
|
|
}
|
|
|
}
|
|
|
- return Promise.resolve(
|
|
|
- [...map.entries()].map(([value, label]) => ({ label, value })),
|
|
|
+
|
|
|
+ const result = await listMedicineCentersMethod(1, 500, query);
|
|
|
+ return toSelectOptions(
|
|
|
+ (result.items ?? []).map((item) => ({
|
|
|
+ label: item.name,
|
|
|
+ value: item.code || item.id,
|
|
|
+ })),
|
|
|
);
|
|
|
}
|