feat(knowledge): Web 病种图片上传 + 小程序症状图展示
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user