feat(knowledge): Web 病种图片上传 + 小程序症状图展示

This commit is contained in:
weijuesen
2026-08-12 12:13:35 +08:00
parent c8aa213626
commit 3bb1efae57
6 changed files with 64 additions and 2 deletions
+1
View File
@@ -16,6 +16,7 @@ export interface Disease {
recurrence?: string; recurrence?: string;
detection?: string; detection?: string;
prevention?: string; prevention?: string;
imageUrl?: string;
sortOrder?: number; sortOrder?: number;
} }
@@ -39,6 +39,19 @@
padding: $spacing-lg; padding: $spacing-lg;
} }
.imageCard {
margin: $spacing-lg $spacing-lg 0;
background: $color-bg-card;
border-radius: $radius-md;
padding: $spacing-md;
box-shadow: $shadow-card;
}
.symptomImage {
width: 100%;
border-radius: $radius-sm;
}
.sectionCard { .sectionCard {
background: $color-bg-card; background: $color-bg-card;
border-radius: $radius-md; border-radius: $radius-md;
+6 -1
View File
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { ScrollView, Text, View } from '@tarojs/components'; import { Image, ScrollView, Text, View } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro'; import Taro, { useRouter } from '@tarojs/taro';
import styles from './index.module.scss'; import styles from './index.module.scss';
import { import {
@@ -86,6 +86,11 @@ const DetailPage: React.FC = () => {
<Text className={styles.heroName}>{disease.name}</Text> <Text className={styles.heroName}>{disease.name}</Text>
<Text className={styles.heroTag}>{CATEGORY_LABELS[disease.category] || disease.category}</Text> <Text className={styles.heroTag}>{CATEGORY_LABELS[disease.category] || disease.category}</Text>
</View> </View>
{disease.imageUrl ? (
<View className={styles.imageCard}>
<Image className={styles.symptomImage} src={disease.imageUrl} mode="widthFix" />
</View>
) : null}
<View className={styles.sectionList}> <View className={styles.sectionList}>
{sections.map((s) => ( {sections.map((s) => (
<View key={s.label} className={styles.sectionCard}> <View key={s.label} className={styles.sectionCard}>
+1
View File
@@ -196,6 +196,7 @@ export interface Disease {
recurrence?: string; recurrence?: string;
detection?: string; detection?: string;
prevention?: string; prevention?: string;
imageUrl?: string;
sortOrder?: number; sortOrder?: number;
} }
+6
View File
@@ -45,3 +45,9 @@ export const listArticles = (params?: any) => get<KnowledgeArticle[]>('/knowledg
export const createArticle = (data: Partial<KnowledgeArticle>) => post<KnowledgeArticle>('/knowledge/articles', data); export const createArticle = (data: Partial<KnowledgeArticle>) => post<KnowledgeArticle>('/knowledge/articles', data);
export const updateArticle = (id: string, data: Partial<KnowledgeArticle>) => patch<KnowledgeArticle>(`/knowledge/articles/${id}`, data); export const updateArticle = (id: string, data: Partial<KnowledgeArticle>) => patch<KnowledgeArticle>(`/knowledge/articles/${id}`, data);
export const deleteArticle = (id: string) => del(`/knowledge/articles/${id}`); export const deleteArticle = (id: string) => del(`/knowledge/articles/${id}`);
export const uploadKnowledgeImage = (file: File) => {
const fd = new FormData();
fd.append('file', file);
return post<{ url: string; name: string; size: number }>('/knowledge/images', fd);
};
+37 -1
View File
@@ -1,5 +1,6 @@
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Switch, Tabs, Tag, message } from 'antd'; import { Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Switch, Tabs, Tag, Upload, message } from 'antd';
import type { UploadFile } from 'antd';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { ProTable, type ActionType, type ProColumns } from '@ant-design/pro-components'; import { ProTable, type ActionType, type ProColumns } from '@ant-design/pro-components';
import { import {
@@ -11,6 +12,7 @@ import {
createArticle, createArticle,
updateArticle, updateArticle,
deleteArticle, deleteArticle,
uploadKnowledgeImage,
type Disease, type Disease,
type KnowledgeArticle, type KnowledgeArticle,
} from '../dal/knowledge'; } from '../dal/knowledge';
@@ -38,6 +40,7 @@ function DiseaseTab() {
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
const [form] = Form.useForm<Partial<Disease>>(); const [form] = Form.useForm<Partial<Disease>>();
const [editing, setEditing] = useState<Disease | null>(null); const [editing, setEditing] = useState<Disease | null>(null);
const [imageList, setImageList] = useState<UploadFile[]>([]);
const columns: ProColumns<Disease>[] = [ const columns: ProColumns<Disease>[] = [
{ title: '序号', valueType: 'indexBorder', search: false, width: 60 }, { title: '序号', valueType: 'indexBorder', search: false, width: 60 },
@@ -69,6 +72,9 @@ function DiseaseTab() {
onClick={() => { onClick={() => {
setEditing(r); setEditing(r);
form.setFieldsValue(r); form.setFieldsValue(r);
setImageList(
r.imageUrl ? [{ uid: '-1', name: '症状图谱', status: 'done', url: r.imageUrl }] : [],
);
setModalOpen(true); setModalOpen(true);
}} }}
> >
@@ -97,6 +103,7 @@ function DiseaseTab() {
setEditing(null); setEditing(null);
form.resetFields(); form.resetFields();
form.setFieldsValue({ category: 'viral', enabled: true, sortOrder: 0 }); form.setFieldsValue({ category: 'viral', enabled: true, sortOrder: 0 });
setImageList([]);
setModalOpen(true); setModalOpen(true);
}; };
@@ -147,6 +154,35 @@ function DiseaseTab() {
options={Object.entries(CATEGORY_LABELS).map(([value, label]) => ({ value, label }))} options={Object.entries(CATEGORY_LABELS).map(([value, label]) => ({ value, label }))}
/> />
</Form.Item> </Form.Item>
<Form.Item label="症状图谱图片">
<Upload
listType="picture-card"
maxCount={1}
accept=".jpg,.jpeg,.png,.webp"
fileList={imageList}
customRequest={async ({ file, onSuccess, onError }) => {
try {
const res = await uploadKnowledgeImage(file as File);
form.setFieldValue('imageUrl', res.url);
setImageList([{ uid: '-1', name: res.name, status: 'done', url: res.url }]);
onSuccess?.(res);
} catch (e) {
onError?.(e as Error);
}
}}
onRemove={() => {
setImageList([]);
form.setFieldValue('imageUrl', undefined);
}}
>
{imageList.length >= 1 ? null : (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}></div>
</div>
)}
</Upload>
</Form.Item>
<Form.Item label="病原" name="pathogen"> <Form.Item label="病原" name="pathogen">
<Input.TextArea rows={2} /> <Input.TextArea rows={2} />
</Form.Item> </Form.Item>