diff --git a/src/api/cases.ts b/src/api/cases.ts index 42c36da3..ae58ae4c 100644 --- a/src/api/cases.ts +++ b/src/api/cases.ts @@ -390,9 +390,9 @@ function normalizeCase(item: unknown, index: number): CaseListItem { title, caseType: getString(record, ['case_type', 'caseType', 'type']), publishStatus, - institutionId: getRelatedId(record, ['institution_id', 'institutionId'], ['institution', 'institution_info', 'institutionInfo']), + institutionId: getRelatedId(record, ['institution_id', 'institutionId', 'institution'], ['institution_info', 'institutionInfo']), institutionName: getRelatedName(record, ['institution_name', 'institutionName', 'organization_name', 'organizationName'], ['institution', 'institution_info', 'institutionInfo']), - departmentId: getRelatedId(record, ['department_id', 'departmentId'], ['department', 'department_info', 'departmentInfo']), + departmentId: getRelatedId(record, ['department_id', 'departmentId', 'department'], ['department_info', 'departmentInfo']), departmentName: getRelatedName(record, ['department_name', 'departmentName'], ['department', 'department_info', 'departmentInfo']), difficulty: getString(record, ['difficulty']), chiefComplaint: getString(record, ['chief_complaint', 'chiefComplaint']), diff --git a/src/api/institutions.ts b/src/api/institutions.ts index 52eb5b4f..86131602 100644 --- a/src/api/institutions.ts +++ b/src/api/institutions.ts @@ -134,6 +134,28 @@ function getBannerUrlFromResponse(data: unknown): string { return '' } +function getInstitutionIdFromResponse(data: unknown): number { + if (!data || typeof data !== 'object') { + return 0 + } + + const record = data as Record + const id = record.id || record.institution_id || record.institutionId + if (typeof id === 'number' && Number.isFinite(id)) { + return id + } + if (typeof id === 'string' && Number.isFinite(Number(id))) { + return Number(id) + } + + const nested = record.data || record.result || record.payload || record.institution + if (nested && typeof nested === 'object') { + return getInstitutionIdFromResponse(nested) + } + + return 0 +} + function normalizeInstitution(item: unknown): InstitutionListItem { const record = item && typeof item === 'object' ? (item as Record) : {} const id = record.id @@ -283,7 +305,7 @@ export async function fetchInstitutions(params: InstitutionListParams): Promise< } } -export async function createInstitution(params: InstitutionMutationParams): Promise { +export async function createInstitution(params: InstitutionMutationParams): Promise { const response = await fetch('/server/api/cms/institutions/', { method: 'POST', headers: { @@ -294,7 +316,14 @@ export async function createInstitution(params: InstitutionMutationParams): Prom body: JSON.stringify(params.payload) }) - return parseMutationResponse(response, '新增机构失败') + const data = await parseMutationResponse(response, '新增机构失败') + const normalized = normalizeInstitution(data) + const id = normalized.id || getInstitutionIdFromResponse(data) + + return { + ...normalized, + id + } } export async function updateInstitution(params: UpdateInstitutionParams): Promise { diff --git a/src/views/CaseReviewView.vue b/src/views/CaseReviewView.vue index dec5b4c9..bd1b4b4e 100644 --- a/src/views/CaseReviewView.vue +++ b/src/views/CaseReviewView.vue @@ -490,8 +490,8 @@ function fillDetailForm(row: CaseListItem, fullData: unknown) { detailForm.title = getString(record, ['title', 'name', 'case_title', 'caseTitle'], row.title) detailForm.case_type = normalizeCaseType(getString(record, ['case_type', 'caseType'], row.caseType), normalizeCaseType(row.caseType, 'traditional')) - detailForm.institution_name = getString(record, ['institution_name', 'institutionName'], row.institutionName || row.institutionId) - detailForm.department_name = getString(record, ['department_name', 'departmentName'], row.departmentName || row.departmentId) + detailForm.institution_name = getString(record, ['institution_name', 'institutionName'], row.institutionName || row.institutionId || getString(record, ['institution'])) + detailForm.department_name = getString(record, ['department_name', 'departmentName'], row.departmentName || row.departmentId || getString(record, ['department'])) detailForm.difficulty = getString(record, ['difficulty'], row.difficulty) detailForm.chief_complaint = getString(record, ['chief_complaint', 'chiefComplaint'], row.chiefComplaint) detailForm.description = getString(record, ['description', 'summary', 'content']) diff --git a/src/views/CasesView.vue b/src/views/CasesView.vue index f1ff4849..70bbff21 100644 --- a/src/views/CasesView.vue +++ b/src/views/CasesView.vue @@ -420,6 +420,7 @@

医院管理

-

维护医院列表,支持按名称、编码、类型和地区查询。

+

维护医院列表,支持按名称、编码和地区查询。

导入模板 @@ -15,11 +15,6 @@
- - - - - 查询 @@ -89,13 +84,6 @@ - - - - - - - @@ -103,12 +91,31 @@ - + + + - + + + @@ -135,7 +142,7 @@ 清空背景图
-

编辑时选择图片会先上传背景图,再保存机构信息;支持 png/jpg/jpeg/webp,最大 5MB。

+

选择图片后保存会自动上传背景图;支持 png/jpg/jpeg/webp,最大 5MB。

@@ -215,7 +222,6 @@ const bannerFile = ref(null) const institutions = ref([]) const filters = reactive({ search: '', - type: '', province: '', city: '' }) @@ -233,10 +239,41 @@ const institutionForm = reactive({ bannerUrl: '' }) -const institutionTypeOptions = [ - { label: '医院 hospital', value: 'hospital' }, - { label: '医学院校 school', value: 'school' }, - { label: '基层机构 primary', value: 'primary' } +const provinceOptions = [ + { name: '北京市', cities: ['北京市'] }, + { name: '天津市', cities: ['天津市'] }, + { name: '上海市', cities: ['上海市'] }, + { name: '重庆市', cities: ['重庆市'] }, + { name: '河北省', cities: ['石家庄市', '唐山市', '秦皇岛市', '邯郸市', '邢台市', '保定市', '张家口市', '承德市', '沧州市', '廊坊市', '衡水市'] }, + { name: '山西省', cities: ['太原市', '大同市', '阳泉市', '长治市', '晋城市', '朔州市', '晋中市', '运城市', '忻州市', '临汾市', '吕梁市'] }, + { name: '内蒙古自治区', cities: ['呼和浩特市', '包头市', '乌海市', '赤峰市', '通辽市', '鄂尔多斯市', '呼伦贝尔市', '巴彦淖尔市', '乌兰察布市'] }, + { name: '辽宁省', cities: ['沈阳市', '大连市', '鞍山市', '抚顺市', '本溪市', '丹东市', '锦州市', '营口市', '阜新市', '辽阳市', '盘锦市', '铁岭市', '朝阳市', '葫芦岛市'] }, + { name: '吉林省', cities: ['长春市', '吉林市', '四平市', '辽源市', '通化市', '白山市', '松原市', '白城市'] }, + { name: '黑龙江省', cities: ['哈尔滨市', '齐齐哈尔市', '鸡西市', '鹤岗市', '双鸭山市', '大庆市', '伊春市', '佳木斯市', '七台河市', '牡丹江市', '黑河市', '绥化市'] }, + { name: '江苏省', cities: ['南京市', '无锡市', '徐州市', '常州市', '苏州市', '南通市', '连云港市', '淮安市', '盐城市', '扬州市', '镇江市', '泰州市', '宿迁市'] }, + { name: '浙江省', cities: ['杭州市', '宁波市', '温州市', '嘉兴市', '湖州市', '绍兴市', '金华市', '衢州市', '舟山市', '台州市', '丽水市'] }, + { name: '安徽省', cities: ['合肥市', '芜湖市', '蚌埠市', '淮南市', '马鞍山市', '淮北市', '铜陵市', '安庆市', '黄山市', '滁州市', '阜阳市', '宿州市', '六安市', '亳州市', '池州市', '宣城市'] }, + { name: '福建省', cities: ['福州市', '厦门市', '莆田市', '三明市', '泉州市', '漳州市', '南平市', '龙岩市', '宁德市'] }, + { name: '江西省', cities: ['南昌市', '景德镇市', '萍乡市', '九江市', '新余市', '鹰潭市', '赣州市', '吉安市', '宜春市', '抚州市', '上饶市'] }, + { name: '山东省', cities: ['济南市', '青岛市', '淄博市', '枣庄市', '东营市', '烟台市', '潍坊市', '济宁市', '泰安市', '威海市', '日照市', '临沂市', '德州市', '聊城市', '滨州市', '菏泽市'] }, + { name: '河南省', cities: ['郑州市', '开封市', '洛阳市', '平顶山市', '安阳市', '鹤壁市', '新乡市', '焦作市', '濮阳市', '许昌市', '漯河市', '三门峡市', '南阳市', '商丘市', '信阳市', '周口市', '驻马店市'] }, + { name: '湖北省', cities: ['武汉市', '黄石市', '十堰市', '宜昌市', '襄阳市', '鄂州市', '荆门市', '孝感市', '荆州市', '黄冈市', '咸宁市', '随州市'] }, + { name: '湖南省', cities: ['长沙市', '株洲市', '湘潭市', '衡阳市', '邵阳市', '岳阳市', '常德市', '张家界市', '益阳市', '郴州市', '永州市', '怀化市', '娄底市'] }, + { name: '广东省', cities: ['广州市', '韶关市', '深圳市', '珠海市', '汕头市', '佛山市', '江门市', '湛江市', '茂名市', '肇庆市', '惠州市', '梅州市', '汕尾市', '河源市', '阳江市', '清远市', '东莞市', '中山市', '潮州市', '揭阳市', '云浮市'] }, + { name: '广西壮族自治区', cities: ['南宁市', '柳州市', '桂林市', '梧州市', '北海市', '防城港市', '钦州市', '贵港市', '玉林市', '百色市', '贺州市', '河池市', '来宾市', '崇左市'] }, + { name: '海南省', cities: ['海口市', '三亚市', '三沙市', '儋州市'] }, + { name: '四川省', cities: ['成都市', '自贡市', '攀枝花市', '泸州市', '德阳市', '绵阳市', '广元市', '遂宁市', '内江市', '乐山市', '南充市', '眉山市', '宜宾市', '广安市', '达州市', '雅安市', '巴中市', '资阳市'] }, + { name: '贵州省', cities: ['贵阳市', '六盘水市', '遵义市', '安顺市', '毕节市', '铜仁市'] }, + { name: '云南省', cities: ['昆明市', '曲靖市', '玉溪市', '保山市', '昭通市', '丽江市', '普洱市', '临沧市'] }, + { name: '西藏自治区', cities: ['拉萨市', '日喀则市', '昌都市', '林芝市', '山南市', '那曲市'] }, + { name: '陕西省', cities: ['西安市', '铜川市', '宝鸡市', '咸阳市', '渭南市', '延安市', '汉中市', '榆林市', '安康市', '商洛市'] }, + { name: '甘肃省', cities: ['兰州市', '嘉峪关市', '金昌市', '白银市', '天水市', '武威市', '张掖市', '平凉市', '酒泉市', '庆阳市', '定西市', '陇南市'] }, + { name: '青海省', cities: ['西宁市', '海东市'] }, + { name: '宁夏回族自治区', cities: ['银川市', '石嘴山市', '吴忠市', '固原市', '中卫市'] }, + { name: '新疆维吾尔自治区', cities: ['乌鲁木齐市', '克拉玛依市', '吐鲁番市', '哈密市'] }, + { name: '香港特别行政区', cities: ['香港特别行政区'] }, + { name: '澳门特别行政区', cities: ['澳门特别行政区'] }, + { name: '台湾省', cities: ['台北市', '新北市', '桃园市', '台中市', '台南市', '高雄市'] } ] const institutionRules = computed(() => { @@ -251,6 +288,7 @@ const institutionRules = computed(() => { }) const institutionDialogTitle = computed(() => (institutionMode.value === 'create' ? '新增机构' : '编辑机构')) const institutionBannerPreview = computed(() => bannerPreviewUrl.value || institutionForm.bannerUrl.trim()) +const cityOptions = computed(() => provinceOptions.find(item => item.name === institutionForm.province)?.cities || []) async function loadInstitutions() { if (!appStore.token) { @@ -263,7 +301,6 @@ async function loadInstitutions() { const result = await fetchInstitutions({ token: appStore.token, search: filters.search, - type: filters.type, province: filters.province, city: filters.city, page: pagination.page @@ -284,7 +321,6 @@ function handleSearch() { function resetFilters() { filters.search = '' - filters.type = '' filters.province = '' filters.city = '' pagination.page = 1 @@ -338,6 +374,23 @@ function resetInstitutionForm() { institutionFormRef.value?.clearValidate() } +function handleProvinceChange(value: string) { + const matched = provinceOptions.find(item => item.name === value) + if (!matched) { + institutionForm.city = '' + return + } + + if (matched.cities.length === 1) { + institutionForm.city = matched.cities[0] + return + } + + if (!matched.cities.includes(institutionForm.city)) { + institutionForm.city = '' + } +} + function buildCreatePayload(): CreateInstitutionPayload { const payload: CreateInstitutionPayload = { code: institutionForm.code.trim(), @@ -384,14 +437,6 @@ function buildUpdatePayload(): InstitutionPayload { } function setInstitutionBannerFile(file: UploadRawFile) { - if (institutionMode.value === 'create') { - bannerFileList.value = [] - bannerFile.value = null - bannerUploadRef.value?.clearFiles() - ElMessage.warning('请先新增机构,保存后编辑时再上传背景图') - return - } - const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp'] if (!allowedTypes.includes(file.type)) { bannerFileList.value = [] @@ -448,14 +493,15 @@ function clearInstitutionBanner() { bannerUploadRef.value?.clearFiles() } -async function uploadPendingBanner() { - if (!bannerFile.value || !editingInstitution.value || !appStore.token) { +async function uploadPendingBanner(institutionId?: number) { + const id = institutionId ?? editingInstitution.value?.id + if (!bannerFile.value || !id || !appStore.token) { return '' } const result = await uploadInstitutionBanner({ token: appStore.token, - id: editingInstitution.value.id, + id, file: bannerFile.value }) if (!result.bannerUrl) { @@ -479,10 +525,21 @@ async function submitInstitutionForm() { try { savingInstitution.value = true if (institutionMode.value === 'create') { - await createInstitution({ + const createdInstitution = await createInstitution({ token: appStore.token, payload: buildCreatePayload() }) + if (bannerFile.value && !createdInstitution.id) { + throw new Error('新增机构成功但未返回机构ID,无法上传背景图') + } + const uploadedBannerUrl = await uploadPendingBanner(createdInstitution.id) + if (uploadedBannerUrl) { + await updateInstitution({ + token: appStore.token, + id: createdInstitution.id, + payload: { banner_url: uploadedBannerUrl } + }) + } ElMessage.success('机构已新增') pagination.page = 1 } else if (editingInstitution.value) { @@ -607,7 +664,6 @@ async function exportCurrentInstitutions() { await exportInstitutions({ token: appStore.token, search: filters.search, - type: filters.type, province: filters.province, city: filters.city })