正在显示
22 个修改的文件
包含
709 行增加
和
32 行删除
@@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
8 | "name": "react18-tsx-neteasemusic", | 8 | "name": "react18-tsx-neteasemusic", |
9 | "version": "0.1.0", | 9 | "version": "0.1.0", |
10 | "dependencies": { | 10 | "dependencies": { |
11 | + "@ant-design/icons": "^5.2.6", | ||
11 | "@reduxjs/toolkit": "^1.9.5", | 12 | "@reduxjs/toolkit": "^1.9.5", |
12 | "@testing-library/jest-dom": "^5.17.0", | 13 | "@testing-library/jest-dom": "^5.17.0", |
13 | "@testing-library/react": "^13.4.0", | 14 | "@testing-library/react": "^13.4.0", |
@@ -3,6 +3,7 @@ | @@ -3,6 +3,7 @@ | ||
3 | "version": "0.1.0", | 3 | "version": "0.1.0", |
4 | "private": true, | 4 | "private": true, |
5 | "dependencies": { | 5 | "dependencies": { |
6 | + "@ant-design/icons": "^5.2.6", | ||
6 | "@reduxjs/toolkit": "^1.9.5", | 7 | "@reduxjs/toolkit": "^1.9.5", |
7 | "@testing-library/jest-dom": "^5.17.0", | 8 | "@testing-library/jest-dom": "^5.17.0", |
8 | "@testing-library/react": "^13.4.0", | 9 | "@testing-library/react": "^13.4.0", |
1 | import hyRequest from "@/service"; | 1 | import hyRequest from "@/service"; |
2 | +import { Send_prescription_formType } from '@/types' | ||
2 | 3 | ||
3 | export const getTopBanner = () => hyRequest.get({ url: '/banner' }) | 4 | export const getTopBanner = () => hyRequest.get({ url: '/banner' }) |
5 | + | ||
6 | +// 公共 | ||
7 | +export const getSendMessage = (params: { phoneNum: string }) => hyRequest.get({ url: '/pet/login/sendMessage', params }) // 发送验证码 | ||
8 | +export const getMessageLogin = (params: any) => hyRequest.get({ url: '/pet/login/messageLogin', params }) // 验证码登录 | ||
9 | +export const updateSend_prescription_form = (params: Send_prescription_formType) => hyRequest.post({ url: '/veterinary/send_prescription_form', params }) // 发送处方单 |
1 | body, textarea, select, input, button { | 1 | body, textarea, select, input, button { |
2 | - font-size: 12px; | ||
3 | - color: #333; | ||
4 | - font-family: Arial, Helvetica, sans-serif; | ||
5 | - background-color: #f5f5f5; | 2 | + // font-size: 12px; |
3 | + // color: #333; | ||
4 | + // font-family: Arial, Helvetica, sans-serif; | ||
5 | + // background-color: #f5f5f5; | ||
6 | } | 6 | } |
7 | 7 | ||
8 | .wrap-v1 { | 8 | .wrap-v1 { |
@@ -60,10 +60,10 @@ body, textarea, select, input, button { | @@ -60,10 +60,10 @@ body, textarea, select, input, button { | ||
60 | } | 60 | } |
61 | 61 | ||
62 | .ant-message .ant-message-notice-content { | 62 | .ant-message .ant-message-notice-content { |
63 | - position: fixed; | ||
64 | - left: 50%; | ||
65 | - transform: translateX(-50%); | ||
66 | - bottom: 60px; | ||
67 | - background-color: rgba(0, 0, 0, .7); | ||
68 | - color: #fff; | 63 | + // position: fixed; |
64 | + // left: 50%; | ||
65 | + // transform: translateX(-50%); | ||
66 | + // bottom: 60px; | ||
67 | + // background-color: rgba(0, 0, 0, .7); | ||
68 | + // color: #fff; | ||
69 | } | 69 | } |
src/components/aa/index.tsx
0 → 100644
1 | +import React, { useRef, useImperativeHandle, Ref } from 'react' | ||
2 | + | ||
3 | +// 定义子组件的接口 | ||
4 | +interface ChildComponentProps { | ||
5 | + // 定义子组件暴露给父组件的方法 | ||
6 | + increment: () => void | ||
7 | + getCount: () => number | ||
8 | +} | ||
9 | + | ||
10 | +const ChildComponent = React.forwardRef((props: ChildComponentProps, ref: Ref<any>) => { | ||
11 | + const internalState = { | ||
12 | + count: 0 | ||
13 | + } | ||
14 | + | ||
15 | + const incrementCount = () => { | ||
16 | + internalState.count += 1 | ||
17 | + } | ||
18 | + | ||
19 | + useImperativeHandle(ref, () => ({ | ||
20 | + increment: incrementCount, | ||
21 | + getCount: () => internalState.count | ||
22 | + })) | ||
23 | + | ||
24 | + return ( | ||
25 | + <div> | ||
26 | + <p>Count: {internalState.count}</p> | ||
27 | + </div> | ||
28 | + ) | ||
29 | +}) | ||
30 | + | ||
31 | +const ParentComponent = () => { | ||
32 | + const childRef = useRef<ChildComponentProps | null>(null) | ||
33 | + | ||
34 | + const handleIncrement = () => { | ||
35 | + if (childRef.current) { | ||
36 | + childRef.current.increment() | ||
37 | + } | ||
38 | + } | ||
39 | + | ||
40 | + const handleGetCount = () => { | ||
41 | + if (childRef.current) { | ||
42 | + const count = childRef.current.getCount() | ||
43 | + alert('Count from child component: ' + count) | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + return ( | ||
48 | + <div> | ||
49 | + <h2>Parent Component</h2> | ||
50 | + <button onClick={handleIncrement}>Increment Count in Child</button> | ||
51 | + <button onClick={handleGetCount}>Get Count from Child</button> | ||
52 | + <ChildComponent | ||
53 | + ref={childRef} | ||
54 | + increment={function (): void { | ||
55 | + throw new Error('Function not implemented.') | ||
56 | + }} | ||
57 | + getCount={function (): number { | ||
58 | + throw new Error('Function not implemented.') | ||
59 | + }} | ||
60 | + /> | ||
61 | + </div> | ||
62 | + ) | ||
63 | +} | ||
64 | + | ||
65 | +export default ParentComponent |
@@ -6,6 +6,9 @@ export const AppHeaderWrapper = styled.div` | @@ -6,6 +6,9 @@ export const AppHeaderWrapper = styled.div` | ||
6 | font-size: 14px; | 6 | font-size: 14px; |
7 | box-sizing: border-box; | 7 | box-sizing: border-box; |
8 | padding: 20px 32px; | 8 | padding: 20px 32px; |
9 | + box-shadow: 5px 5px 15px rgba(0,0,0,0.4); | ||
10 | + z-index: 9999999999; | ||
11 | + position: relative; | ||
9 | .title{ | 12 | .title{ |
10 | color: #fff; | 13 | color: #fff; |
11 | font-size: 22px; | 14 | font-size: 22px; |
@@ -8,9 +8,11 @@ interface IProps { | @@ -8,9 +8,11 @@ interface IProps { | ||
8 | 8 | ||
9 | const AppLoading: FC<IProps> = memo(() => { | 9 | const AppLoading: FC<IProps> = memo(() => { |
10 | return ( | 10 | return ( |
11 | - <Space size="middle"> | ||
12 | - <Spin size="large" /> | ||
13 | - </Space> | 11 | + <div className="flexC" style={{ width: '100%' }}> |
12 | + <Space size="middle"> | ||
13 | + <Spin size="large" /> | ||
14 | + </Space> | ||
15 | + </div> | ||
14 | ) | 16 | ) |
15 | }) | 17 | }) |
16 | 18 |
src/hooks/debounceBy.ts
0 → 100644
1 | +// 按钮点击防抖 | ||
2 | +/** | ||
3 | + * @使用方法 | ||
4 | + * @vue2 | ||
5 | + * 点击事件名:DebounceBy(function(args){ 写你自己的业务代码 },3000) | ||
6 | + * @vue3 | ||
7 | + * const 点击事件名 = DebounceBy(async (args) => { 写你自己的业务代码 }, 3000) | ||
8 | + */ | ||
9 | +export const DebounceBy = (fn: any, t: number = 500) => { | ||
10 | + let delay = t || 500 | ||
11 | + let timer: any | ||
12 | + return function () { | ||
13 | + let args = arguments | ||
14 | + if (timer) { | ||
15 | + clearTimeout(timer) | ||
16 | + } | ||
17 | + | ||
18 | + let callNow = !timer | ||
19 | + | ||
20 | + timer = setTimeout(() => { | ||
21 | + timer = null | ||
22 | + }, delay) | ||
23 | + | ||
24 | + if (callNow) fn(args) | ||
25 | + // if (callNow) fn.apply(this, args) | ||
26 | + } | ||
27 | +} | ||
28 | + | ||
29 | +// 节流:防止高频触发 | ||
30 | +export const Throttle = (fn: any, delay: number) => { | ||
31 | + let flag = true | ||
32 | + return function () { | ||
33 | + if (flag) { | ||
34 | + setTimeout(() => { | ||
35 | + // fn.call(this) | ||
36 | + flag = true | ||
37 | + }, delay) | ||
38 | + } | ||
39 | + flag = false | ||
40 | + } | ||
41 | +} |
src/hooks/useTimeChange copy.ts
0 → 100644
1 | +import { useRef, useState } from 'react' | ||
2 | + | ||
3 | +/** | ||
4 | + * @import import useTimeHandler from '../../hooks/useTimeChange' | ||
5 | + * @use const [ countdown, startCountdown ] = useTimeHandler() startCountdown(handler) | ||
6 | + * @param executeHandler function | ||
7 | + * @returns countdown , startCountDown => void | ||
8 | + */ | ||
9 | + | ||
10 | +type ExecuteHandlerType = () => void | ||
11 | + | ||
12 | +type useCountdownType = [number, (fn: ExecuteHandlerType) => void] | ||
13 | + | ||
14 | +const useCountdown = (): useCountdownType => { | ||
15 | + let [countdown, setCountdown] = useState(60) | ||
16 | + const newC = useRef(60) | ||
17 | + | ||
18 | + const timeHandler = () => { | ||
19 | + let timer = setInterval(() => { | ||
20 | + console.log(countdown, '倒计时') | ||
21 | + // let newC = countdown - 1 | ||
22 | + setCountdown(newC.current--) | ||
23 | + if (newC.current === 0) { | ||
24 | + newC.current = 60 | ||
25 | + setCountdown(60) | ||
26 | + clearInterval(timer) | ||
27 | + } | ||
28 | + }, 1000) | ||
29 | + } | ||
30 | + | ||
31 | + const startCountdown = (executeHandler: ExecuteHandlerType) => { | ||
32 | + if (countdown !== 60) return | ||
33 | + executeHandler() | ||
34 | + timeHandler() | ||
35 | + } | ||
36 | + | ||
37 | + return [countdown, startCountdown] | ||
38 | +} | ||
39 | + | ||
40 | +export default useCountdown |
src/hooks/useTimeChange.ts
0 → 100644
1 | +import { useRef, useState } from 'react' | ||
2 | + | ||
3 | +/** | ||
4 | + * @import import useTimeHandler from '../../hooks/useTimeChange' | ||
5 | + * @use const [ countdown, startCountdown ] = useTimeHandler() startCountdown(handler) | ||
6 | + * @param executeHandler function | ||
7 | + * @returns countdown , startCountDown => void | ||
8 | + */ | ||
9 | + | ||
10 | +type ExecuteHandlerType = () => void | ||
11 | + | ||
12 | +type useCountdownType = [number, (fn: ExecuteHandlerType) => void] | ||
13 | + | ||
14 | +const useCountdown = (): useCountdownType => { | ||
15 | + let [countdown, setCountdown] = useState(60) | ||
16 | + | ||
17 | + const timeHandler = () => { | ||
18 | + let timer = setInterval(() => { | ||
19 | + let newC = countdown -= 1 | ||
20 | + setCountdown(newC--) | ||
21 | + if (countdown === 0) { | ||
22 | + setCountdown(60) | ||
23 | + clearInterval(timer) | ||
24 | + } | ||
25 | + }, 1000) | ||
26 | + } | ||
27 | + | ||
28 | + const startCountdown = (executeHandler: ExecuteHandlerType) => { | ||
29 | + if (countdown !== 60) return | ||
30 | + timeHandler() | ||
31 | + executeHandler() | ||
32 | + } | ||
33 | + | ||
34 | + return [countdown, startCountdown] | ||
35 | +} | ||
36 | + | ||
37 | +export default useCountdown |
@@ -5,7 +5,9 @@ import { BASE_URL, TIME_OUT } from './request/config' | @@ -5,7 +5,9 @@ import { BASE_URL, TIME_OUT } from './request/config' | ||
5 | const hyRequest = new HYRequest({ | 5 | const hyRequest = new HYRequest({ |
6 | baseURL: BASE_URL, | 6 | baseURL: BASE_URL, |
7 | timeout: TIME_OUT, | 7 | timeout: TIME_OUT, |
8 | - headers: {}, | 8 | + headers: { |
9 | + 'X-Access-Token': localStorage.getItem('token') || '' | ||
10 | + }, | ||
9 | interceptors: { | 11 | interceptors: { |
10 | requestInterceptor: (config) => { | 12 | requestInterceptor: (config) => { |
11 | return config | 13 | return config |
src/types/params.d.ts
0 → 100644
1 | +export interface PhoneCodeLoginParamsType { | ||
2 | + phoneNum: string | ||
3 | + code: string | ||
4 | +} | ||
5 | + | ||
6 | +export interface Send_prescription_formType { | ||
7 | + /** | ||
8 | + * 处方药 | ||
9 | + */ | ||
10 | + drugList: DrugList[]; | ||
11 | + /** | ||
12 | + * 问诊订单id | ||
13 | + */ | ||
14 | + id?: string; | ||
15 | + /** | ||
16 | + * 处方单金额 | ||
17 | + */ | ||
18 | + prescriptionAmount: number; | ||
19 | + /** | ||
20 | + * 处方单文件 | ||
21 | + */ | ||
22 | + prescriptionFile?: string; | ||
23 | + /** | ||
24 | + * 问诊病情描述 | ||
25 | + */ | ||
26 | + prescriptionForm: string; | ||
27 | + [property: string]: any; | ||
28 | +} | ||
29 | + | ||
30 | +export interface DrugList { | ||
31 | + /** | ||
32 | + * 单价 | ||
33 | + */ | ||
34 | + amount: string; | ||
35 | + /** | ||
36 | + * 说明 | ||
37 | + */ | ||
38 | + des: string; | ||
39 | + /** | ||
40 | + * 药物名称 | ||
41 | + */ | ||
42 | + name: string; | ||
43 | + /** | ||
44 | + * 数量 | ||
45 | + */ | ||
46 | + num: string; | ||
47 | + [property: string]: any; | ||
48 | +} |
@@ -13,6 +13,12 @@ export interface PageListType<PageListItemType> { | @@ -13,6 +13,12 @@ export interface PageListType<PageListItemType> { | ||
13 | size: number | 13 | size: number |
14 | } | 14 | } |
15 | 15 | ||
16 | +export interface PhoneCodeLoginType { | ||
17 | + im_token: string | ||
18 | + userId: string | ||
19 | + token: string | ||
20 | +} | ||
21 | + | ||
16 | export interface ConsultationOrderRecordsType { | 22 | export interface ConsultationOrderRecordsType { |
17 | id: string; | 23 | id: string; |
18 | order_no: string; | 24 | order_no: string; |
1 | +import React, { memo, useState, forwardRef, useImperativeHandle } from 'react' | ||
2 | +import type { FC, ReactNode } from 'react' | ||
3 | +import { Input, Modal, Upload } from 'antd' | ||
4 | +import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons' | ||
5 | +import type { RcFile, UploadProps } from 'antd/es/upload' | ||
6 | +import type { UploadFile } from 'antd/es/upload/interface' | ||
7 | +import { SendPrescriptionWrapper } from '../style' | ||
8 | +import { BASE_URL } from '@/service/request/config' | ||
9 | +import type { Send_prescription_formType, DrugList } from '@/types' | ||
10 | +// /sys/common/appUpload | ||
11 | +const { TextArea } = Input | ||
12 | + | ||
13 | +interface IProps { | ||
14 | + children?: ReactNode | ||
15 | + ref?: any | ||
16 | +} | ||
17 | + | ||
18 | +const getBase64 = (file: RcFile): Promise<string> => | ||
19 | + new Promise((resolve, reject) => { | ||
20 | + const reader = new FileReader() | ||
21 | + reader.readAsDataURL(file) | ||
22 | + reader.onload = () => resolve(reader.result as string) | ||
23 | + reader.onerror = (error) => reject(error) | ||
24 | + }) | ||
25 | + | ||
26 | +const SendPrescription: FC<IProps> = memo( | ||
27 | + forwardRef<HTMLDivElement, IProps>((props, ref) => { | ||
28 | + const [previewOpen, setPreviewOpen] = useState(false) | ||
29 | + const [previewImage, setPreviewImage] = useState('') | ||
30 | + const [previewTitle, setPreviewTitle] = useState('') | ||
31 | + const [fileList, setFileList] = useState<UploadFile[]>([]) | ||
32 | + | ||
33 | + const [Send_prescription_formData, setSend_prescription_formTypeData] = useState<Send_prescription_formType>({ | ||
34 | + drugList: [{ amount: '', des: '', name: '', num: '' }], | ||
35 | + prescriptionForm: '', | ||
36 | + prescriptionAmount: 0 | ||
37 | + }) | ||
38 | + | ||
39 | + const addFormItemHandler = () => { | ||
40 | + const setdata = { ...Send_prescription_formData, drugList: [...Send_prescription_formData.drugList, { amount: '', des: '', name: '', num: '' }] } | ||
41 | + | ||
42 | + setSend_prescription_formTypeData(setdata) | ||
43 | + } | ||
44 | + | ||
45 | + const removeFormItemHandler = (index: number) => { | ||
46 | + if (Send_prescription_formData.drugList.length <= 1) return | ||
47 | + | ||
48 | + const setdata = { ...Send_prescription_formData, drugList: [...Send_prescription_formData.drugList].filter((_, idx) => idx !== index) } | ||
49 | + | ||
50 | + setSend_prescription_formTypeData(setdata) | ||
51 | + } | ||
52 | + | ||
53 | + const changeFormItemValueHandler = (index: number, value: string | number, key: string) => { | ||
54 | + const setdata = { | ||
55 | + ...Send_prescription_formData, | ||
56 | + drugList: Send_prescription_formData.drugList.map((_, idx) => (idx === index ? { ..._, [key]: value } : _)) | ||
57 | + } | ||
58 | + | ||
59 | + setSend_prescription_formTypeData(setdata) | ||
60 | + } | ||
61 | + | ||
62 | + const handleCancel = () => setPreviewOpen(false) | ||
63 | + | ||
64 | + const handlePreview = async (file: UploadFile) => { | ||
65 | + if (!file.url && !file.preview) { | ||
66 | + file.preview = await getBase64(file.originFileObj as RcFile) | ||
67 | + } | ||
68 | + | ||
69 | + setPreviewImage(file.url || (file.preview as string)) | ||
70 | + setPreviewOpen(true) | ||
71 | + setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1)) | ||
72 | + } | ||
73 | + | ||
74 | + const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => setFileList(newFileList) | ||
75 | + | ||
76 | + useImperativeHandle<HTMLDivElement, any>(ref, () => ({ | ||
77 | + Send_prescription_formData, | ||
78 | + fileList | ||
79 | + })) | ||
80 | + | ||
81 | + const uploadButton = ( | ||
82 | + <div> | ||
83 | + <PlusOutlined /> | ||
84 | + <div style={{ marginTop: 8 }}>上传证明</div> | ||
85 | + </div> | ||
86 | + ) | ||
87 | + | ||
88 | + return ( | ||
89 | + <SendPrescriptionWrapper ref={ref}> | ||
90 | + <div className="title">问诊病情</div> | ||
91 | + <TextArea | ||
92 | + value={Send_prescription_formData.prescriptionForm} | ||
93 | + onChange={(e) => setSend_prescription_formTypeData({ ...Send_prescription_formData, prescriptionForm: e.target.value })} | ||
94 | + placeholder="请输入问诊病情" | ||
95 | + autoSize={{ minRows: 3, maxRows: 6 }} | ||
96 | + className="title" | ||
97 | + /> | ||
98 | + <div className="title">处方药</div> | ||
99 | + {Send_prescription_formData.drugList.map((_, index) => ( | ||
100 | + <div className="flexX" key={index}> | ||
101 | + <div className="lefticon" onClick={() => removeFormItemHandler(index)}> | ||
102 | + <MinusCircleOutlined className="grayIcon" /> | ||
103 | + </div> | ||
104 | + <div> | ||
105 | + <Input | ||
106 | + value={Send_prescription_formData.drugList[index].name} | ||
107 | + onChange={(e) => changeFormItemValueHandler(index, e.target.value, 'name')} | ||
108 | + className="title" | ||
109 | + placeholder="请输入药品名称" | ||
110 | + /> | ||
111 | + <div className="flexA"> | ||
112 | + <Input | ||
113 | + value={Send_prescription_formData.drugList[index].amount} | ||
114 | + onChange={(e) => changeFormItemValueHandler(index, e.target.value, 'amount')} | ||
115 | + className="title" | ||
116 | + placeholder="请输入药品价格" | ||
117 | + /> | ||
118 | + <Input | ||
119 | + value={Send_prescription_formData.drugList[index].num} | ||
120 | + onChange={(e) => changeFormItemValueHandler(index, e.target.value, 'num')} | ||
121 | + className="title" | ||
122 | + placeholder="请输入药品数量" | ||
123 | + /> | ||
124 | + </div> | ||
125 | + <TextArea | ||
126 | + value={Send_prescription_formData.drugList[index].des} | ||
127 | + onChange={(e) => changeFormItemValueHandler(index, e.target.value, 'des')} | ||
128 | + className="title" | ||
129 | + placeholder="请输入说明" | ||
130 | + autoSize={{ minRows: 3, maxRows: 6 }} | ||
131 | + /> | ||
132 | + </div> | ||
133 | + </div> | ||
134 | + ))} | ||
135 | + <div className="add flexC" onClick={addFormItemHandler}> | ||
136 | + <PlusCircleOutlined className="blueIcon" /> | ||
137 | + <span>添加处方药</span> | ||
138 | + </div> | ||
139 | + <div className="title">处方单证明:</div> | ||
140 | + <Upload action={BASE_URL + '/sys/common/appUpload'} listType="picture-card" fileList={fileList} onPreview={handlePreview} onChange={handleChange}> | ||
141 | + {fileList.length >= 3 ? null : uploadButton} | ||
142 | + </Upload> | ||
143 | + <Modal open={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel}> | ||
144 | + <img alt="example" style={{ width: '100%' }} src={previewImage} /> | ||
145 | + </Modal> | ||
146 | + <div className="title">总金额:¥{Send_prescription_formData.prescriptionAmount}</div> | ||
147 | + </SendPrescriptionWrapper> | ||
148 | + ) | ||
149 | + }) | ||
150 | +) | ||
151 | + | ||
152 | +export default SendPrescription |
1 | +import React, { memo } from 'react' | ||
2 | +import type { FC, ReactNode } from 'react' | ||
3 | +import { Image } from 'antd' | ||
4 | +import { ViewPrescriptionWrapper } from '../style' | ||
5 | + | ||
6 | +interface IProps { | ||
7 | + children?: ReactNode | ||
8 | +} | ||
9 | + | ||
10 | +const ViewPrescription: FC<IProps> = memo(() => { | ||
11 | + return ( | ||
12 | + <ViewPrescriptionWrapper> | ||
13 | + <div className="title">问诊病情:</div> | ||
14 | + <div className="content">即该不低加造年周消养明价切公没家管发七议性原提何们领从很己发战</div> | ||
15 | + <div className="title">处方药:</div> | ||
16 | + <div className="listbox"> | ||
17 | + <div className="itemo"> | ||
18 | + <div className="flexJ black"> | ||
19 | + <div>药品名称</div> | ||
20 | + <div>¥3.00</div> | ||
21 | + </div> | ||
22 | + <div className="flexJ gray"> | ||
23 | + <div>外用,一日3次,每次10毫升</div> | ||
24 | + <div>x2</div> | ||
25 | + </div> | ||
26 | + </div> | ||
27 | + </div> | ||
28 | + <div className="title">处方单证明:</div> | ||
29 | + <Image width={120} src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" className="title" /> | ||
30 | + <div className="black">总金额:¥26.00</div> | ||
31 | + </ViewPrescriptionWrapper> | ||
32 | + ) | ||
33 | +}) | ||
34 | + | ||
35 | +export default ViewPrescription |
src/views/discover/com/ViewSymptom/index.tsx
0 → 100644
1 | +import React, { memo } from 'react' | ||
2 | +import type { FC, ReactNode } from 'react' | ||
3 | +import { Avatar, Image } from 'antd' | ||
4 | +import { ViewSymptomWrapper } from '../style' | ||
5 | + | ||
6 | +interface IProps { | ||
7 | + children?: ReactNode | ||
8 | +} | ||
9 | + | ||
10 | +const url = 'https://p1.ssl.qhmsg.com/dr/270_500_/t010c2d50907f0a7b9c.png' | ||
11 | + | ||
12 | +const ViewSymptom: FC<IProps> = memo(() => { | ||
13 | + return ( | ||
14 | + <ViewSymptomWrapper> | ||
15 | + <div className="flexJ"> | ||
16 | + <div className="flexA top"> | ||
17 | + <Avatar size="large" src={<img src={url} alt="avatar" />} /> | ||
18 | + <div className="username">卡卡罗特</div> | ||
19 | + <div className="tag">图文问诊</div> | ||
20 | + </div> | ||
21 | + <div className="money">预计收入:¥26.00</div> | ||
22 | + </div> | ||
23 | + <div className="row"> | ||
24 | + 就诊宠物:<span>蓝猫/2岁/女/绝育/1kg</span> | ||
25 | + </div> | ||
26 | + <div className="row"> | ||
27 | + 免疫情况:<span>不详</span> | ||
28 | + </div> | ||
29 | + <div className="row"> | ||
30 | + 喂养方式:<span>自制杂粮</span> | ||
31 | + </div> | ||
32 | + <div className="row"> | ||
33 | + 洗澡频次:<span>一周一次</span> | ||
34 | + </div> | ||
35 | + <div className="row"> | ||
36 | + 出现症状:<span>其他</span> | ||
37 | + </div> | ||
38 | + <div className="row"> | ||
39 | + 症状时间:<span>{`<7天`}</span> | ||
40 | + </div> | ||
41 | + <div className="row"> | ||
42 | + 症状描述:<span>快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦快死了哦</span> | ||
43 | + </div> | ||
44 | + <div className="row"> | ||
45 | + <div>上传图片</div> | ||
46 | + <Image width={120} src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" /> | ||
47 | + </div> | ||
48 | + </ViewSymptomWrapper> | ||
49 | + ) | ||
50 | +}) | ||
51 | + | ||
52 | +export default ViewSymptom |
src/views/discover/com/style.ts
0 → 100644
1 | +import styled from "styled-components"; | ||
2 | + | ||
3 | +export const SendPrescriptionWrapper = styled.div` | ||
4 | + max-height: 440px; | ||
5 | + overflow: auto; | ||
6 | + .title{ | ||
7 | + color: #323233; | ||
8 | + font-size: 15px; | ||
9 | + font-weight: 700; | ||
10 | + margin: 4px 0; | ||
11 | + } | ||
12 | + .lefticon{ | ||
13 | + padding-top: 10px; | ||
14 | + box-sizing: border-box; | ||
15 | + margin-right: 5px; | ||
16 | + } | ||
17 | + .grayIcon{ | ||
18 | + font-size: 18px; | ||
19 | + color: #000; | ||
20 | + } | ||
21 | + .add{ | ||
22 | + width: 100%; | ||
23 | + box-sizing: border-box; | ||
24 | + padding: 13px 0; | ||
25 | + background: #F3FFFF; | ||
26 | + .blueIcon{ | ||
27 | + font-size: 18px; | ||
28 | + color: #09b9d3; | ||
29 | + margin-right: 3px; | ||
30 | + } | ||
31 | + span{ | ||
32 | + color: #05b8d2; | ||
33 | + font-size: 15px; | ||
34 | + font-weight: 700; | ||
35 | + } | ||
36 | + } | ||
37 | +` | ||
38 | + | ||
39 | +export const ViewPrescriptionWrapper = styled.div` | ||
40 | + max-height: 440px; | ||
41 | + overflow: auto; | ||
42 | + .title{ | ||
43 | + color: #323233; | ||
44 | + font-size: 14px; | ||
45 | + font-weight: 700; | ||
46 | + margin-bottom: 8px; | ||
47 | + } | ||
48 | + .content{ | ||
49 | + color: #323233; | ||
50 | + font-size: 14px; | ||
51 | + } | ||
52 | + .itemo{ | ||
53 | + margin-bottom: 20px; | ||
54 | + } | ||
55 | + .listbox{ | ||
56 | + box-sizing: border-box; | ||
57 | + padding: 16px 14px; | ||
58 | + background: #F8F8FA; | ||
59 | + border-radius: 24px; | ||
60 | + margin-bottom: 12px; | ||
61 | + } | ||
62 | + .black{ | ||
63 | + color: #323233; | ||
64 | + font-size: 14px; | ||
65 | + font-weight: 700; | ||
66 | + margin-bottom: 4px; | ||
67 | + } | ||
68 | + .gray{ | ||
69 | + color: #999999; | ||
70 | + font-size: 12px; | ||
71 | + font-weight: 700; | ||
72 | + } | ||
73 | +` | ||
74 | + | ||
75 | +export const ViewSymptomWrapper = styled.div` | ||
76 | + max-height: 440px; | ||
77 | + overflow: auto; | ||
78 | + .top{ | ||
79 | + margin-bottom: 12px; | ||
80 | + } | ||
81 | + .username{ | ||
82 | + color: #242424; | ||
83 | + font-size: 16px; | ||
84 | + font-weight: 700; | ||
85 | + margin: 0 10px; | ||
86 | + } | ||
87 | + .tag{ | ||
88 | + border-radius: 12px; | ||
89 | + background: #05B8D2; | ||
90 | + color: #fff; | ||
91 | + font-size: 12px; | ||
92 | + font-weight: 700; | ||
93 | + box-sizing: border-box; | ||
94 | + padding: 2px 7px; | ||
95 | + } | ||
96 | + .money{ | ||
97 | + color: #f33f2e; | ||
98 | + font-size: 15px; | ||
99 | + font-weight: 700; | ||
100 | + } | ||
101 | + .row{ | ||
102 | + margin-bottom: 12px; | ||
103 | + color: #323233; | ||
104 | + font-size: 28rpx; | ||
105 | + font-weight: 700; | ||
106 | + span{ | ||
107 | + color:#666666; | ||
108 | + } | ||
109 | + } | ||
110 | +` |
1 | -import hyRequest from '@/service' | ||
2 | -import React, { memo, useEffect, useState } from 'react' | 1 | +import React, { memo, useEffect, useState, useRef, ElementRef } from 'react' |
3 | import type { FC, ReactNode } from 'react' | 2 | import type { FC, ReactNode } from 'react' |
4 | -import { Tabs, Avatar, Space, Divider, Button, Modal, Pagination } from 'antd' | 3 | +import { Tabs, Avatar, Space, Divider, Button, Modal, Pagination, Carousel } from 'antd' |
5 | import type { TabsProps } from 'antd' | 4 | import type { TabsProps } from 'antd' |
6 | import { ConsultationOrderWrapper, ConsultationOrderItemWrapper } from './styled' | 5 | import { ConsultationOrderWrapper, ConsultationOrderItemWrapper } from './styled' |
7 | import { useAppDispatch, useAppSelector, shallowEqualApp } from '@/store' | 6 | import { useAppDispatch, useAppSelector, shallowEqualApp } from '@/store' |
7 | +import { updateSend_prescription_form } from '@/api' | ||
8 | +import { Send_prescription_formType } from '@/types' | ||
8 | import { fetchOrderDataAction } from '@/store/modules/order' | 9 | import { fetchOrderDataAction } from '@/store/modules/order' |
10 | +import SendPrescription from '../../com/SendPrescription' | ||
11 | +import ViewPrescription from '../../com/ViewPrescription' | ||
12 | +import ViewSymptom from '../../com/ViewSymptom' | ||
9 | 13 | ||
10 | interface IProps { | 14 | interface IProps { |
11 | children?: ReactNode | 15 | children?: ReactNode |
@@ -18,13 +22,28 @@ const ShowOrderComHandler: FC<IProps> = (props) => { | @@ -18,13 +22,28 @@ const ShowOrderComHandler: FC<IProps> = (props) => { | ||
18 | const { orderList } = props | 22 | const { orderList } = props |
19 | const [open, setOpenHandler] = useState(false) | 23 | const [open, setOpenHandler] = useState(false) |
20 | const [confirmLoading, setConfirmLoading] = useState(false) | 24 | const [confirmLoading, setConfirmLoading] = useState(false) |
25 | + const [modalTitle, setModalTitle] = useState<string>(' ') | ||
26 | + const [modalFlag, setModalFlag] = useState<number>(0) | ||
27 | + | ||
28 | + const SendPrescriptionRef = useRef<ElementRef<typeof SendPrescription> | null>(null) | ||
21 | 29 | ||
22 | const showModalHandler = (flag: number) => { | 30 | const showModalHandler = (flag: number) => { |
23 | console.log(flag, '按钮状态') | 31 | console.log(flag, '按钮状态') |
32 | + setModalFlag(flag) | ||
33 | + setModalTitle({ 1: '查看症状', 2: '发送处方单', 3: '查看处方单' }[flag] as string) | ||
24 | setOpenHandler(true) | 34 | setOpenHandler(true) |
25 | } | 35 | } |
26 | 36 | ||
27 | - const handleOk = () => { | 37 | + const handleOk = async () => { |
38 | + if (modalFlag === 2) { | ||
39 | + const instance = SendPrescriptionRef.current as any | ||
40 | + await updateSend_prescription_form({ | ||
41 | + ...instance.Send_prescription_formData, | ||
42 | + drugList: JSON.stringify(instance.Send_prescription_formData.drugList), | ||
43 | + prescriptionFile: instance.fileList.map((_: any) => _.response.message).join() | ||
44 | + }) | ||
45 | + } | ||
46 | + console.log(SendPrescriptionRef.current, 'SendPrescription 子组件实例') | ||
28 | setConfirmLoading(true) | 47 | setConfirmLoading(true) |
29 | setTimeout(() => { | 48 | setTimeout(() => { |
30 | setOpenHandler(false) | 49 | setOpenHandler(false) |
@@ -34,12 +53,14 @@ const ShowOrderComHandler: FC<IProps> = (props) => { | @@ -34,12 +53,14 @@ const ShowOrderComHandler: FC<IProps> = (props) => { | ||
34 | 53 | ||
35 | const handleCancel = () => { | 54 | const handleCancel = () => { |
36 | console.log('Clicked cancel button') | 55 | console.log('Clicked cancel button') |
56 | + | ||
37 | setOpenHandler(false) | 57 | setOpenHandler(false) |
38 | } | 58 | } |
59 | + | ||
39 | return ( | 60 | return ( |
40 | <ConsultationOrderItemWrapper> | 61 | <ConsultationOrderItemWrapper> |
41 | - <Modal title="Title" open={open} onOk={handleOk} cancelText="关闭" okText="确认" confirmLoading={confirmLoading} onCancel={handleCancel}> | ||
42 | - <p>132</p> | 62 | + <Modal title={modalTitle} open={open} onOk={handleOk} cancelText="关闭" okText="确认" confirmLoading={confirmLoading} onCancel={handleCancel}> |
63 | + {{ 1: <ViewSymptom />, 2: <SendPrescription ref={SendPrescriptionRef} />, 3: <ViewPrescription /> }[modalFlag]} | ||
43 | </Modal> | 64 | </Modal> |
44 | {orderList?.length && | 65 | {orderList?.length && |
45 | orderList.map((_, index) => ( | 66 | orderList.map((_, index) => ( |
@@ -89,7 +110,7 @@ const ConsultationOrder: FC<IProps> = memo(() => { | @@ -89,7 +110,7 @@ const ConsultationOrder: FC<IProps> = memo(() => { | ||
89 | const dispatch = useAppDispatch() | 110 | const dispatch = useAppDispatch() |
90 | 111 | ||
91 | useEffect(() => { | 112 | useEffect(() => { |
92 | - dispatch(fetchOrderDataAction()) | 113 | + // dispatch(fetchOrderDataAction()) |
93 | }, []) | 114 | }, []) |
94 | 115 | ||
95 | const { banners } = useAppSelector( | 116 | const { banners } = useAppSelector( |
@@ -7,6 +7,7 @@ export const ConsultationOrderWrapper = styled.div` | @@ -7,6 +7,7 @@ export const ConsultationOrderWrapper = styled.div` | ||
7 | height: calc(100vh - 65px); | 7 | height: calc(100vh - 65px); |
8 | overflow: auto; | 8 | overflow: auto; |
9 | position: relative; | 9 | position: relative; |
10 | + background: #f5f5f5; | ||
10 | .title{ | 11 | .title{ |
11 | font-size: 18px; | 12 | font-size: 18px; |
12 | font-weight: 700; | 13 | font-weight: 700; |
1 | import React, { memo, useState } from 'react' | 1 | import React, { memo, useState } from 'react' |
2 | import type { FC, ReactNode } from 'react' | 2 | import type { FC, ReactNode } from 'react' |
3 | import { LoginWrapper } from './style' | 3 | import { LoginWrapper } from './style' |
4 | -import { Input, Button } from 'antd' | 4 | +import { Input, Button, message } from 'antd' |
5 | import { useNavigate } from 'react-router-dom' | 5 | import { useNavigate } from 'react-router-dom' |
6 | - | 6 | +import useTimeHandler from '@/hooks/useTimeChange' |
7 | +import { DebounceBy } from '@/hooks/debounceBy' | ||
8 | +import { getSendMessage, getMessageLogin } from '@/api' | ||
9 | +import { PhoneCodeLoginParamsType, PhoneCodeLoginType } from '@/types' | ||
10 | +message.config({ | ||
11 | + top: 400 | ||
12 | +}) | ||
7 | interface IProps { | 13 | interface IProps { |
8 | children?: ReactNode | 14 | children?: ReactNode |
9 | } | 15 | } |
@@ -11,26 +17,71 @@ interface IProps { | @@ -11,26 +17,71 @@ interface IProps { | ||
11 | const Login: FC<IProps> = memo(() => { | 17 | const Login: FC<IProps> = memo(() => { |
12 | const naviate = useNavigate() | 18 | const naviate = useNavigate() |
13 | 19 | ||
14 | - const loginHandler = () => { | 20 | + const [loadingState, setLoadingState] = useState<boolean>(false) |
21 | + | ||
22 | + const [messageApi, contextHolder] = message.useMessage() | ||
23 | + | ||
24 | + const [form, setForm] = useState<PhoneCodeLoginParamsType>({ phoneNum: '', code: '10086' }) | ||
25 | + | ||
26 | + const [countdown, startCountdown] = useTimeHandler() | ||
27 | + | ||
28 | + const warning = (content: string) => messageApi.open({ type: 'warning', content: content }) | ||
29 | + | ||
30 | + const success = (content: string) => messageApi.open({ type: 'success', content: content }) | ||
31 | + | ||
32 | + const sendCodeHandler = DebounceBy(() => { | ||
33 | + if (!/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1589]))\d{8}$/.test(form.phoneNum)) | ||
34 | + return warning('请填写正确的手机号') | ||
35 | + | ||
36 | + startCountdown(async () => { | ||
37 | + await getSendMessage({ phoneNum: form.phoneNum }) | ||
38 | + | ||
39 | + success('发送成功') | ||
40 | + }) | ||
41 | + }, 500) | ||
42 | + | ||
43 | + const loginHandler = async () => { | ||
44 | + if (!/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1589]))\d{8}$/.test(form.phoneNum)) | ||
45 | + return warning('请填写正确的手机号') | ||
46 | + | ||
15 | setLoadingState(true) | 47 | setLoadingState(true) |
16 | 48 | ||
17 | - setTimeout(() => { | ||
18 | - setLoadingState(false) | 49 | + const { result }: { result: PhoneCodeLoginType } = await getMessageLogin(form) |
50 | + | ||
51 | + localStorage.setItem('token', result.token) | ||
52 | + | ||
53 | + localStorage.setItem('userId', result.userId) | ||
54 | + | ||
55 | + localStorage.setItem('im_token', result.im_token) | ||
19 | 56 | ||
57 | + success('登录成功') | ||
58 | + | ||
59 | + setTimeout(() => { | ||
20 | naviate('/discover') | 60 | naviate('/discover') |
21 | - }, 2000) | ||
22 | - } | ||
23 | 61 | ||
24 | - const [loadingState, setLoadingState] = useState<boolean>(false) | 62 | + setLoadingState(false) |
63 | + }, 1000) | ||
64 | + } | ||
25 | 65 | ||
26 | return ( | 66 | return ( |
27 | <LoginWrapper> | 67 | <LoginWrapper> |
68 | + {contextHolder} | ||
28 | <div className="loginBox"> | 69 | <div className="loginBox"> |
29 | <div className="title flexC">宠物问诊-兽医端</div> | 70 | <div className="title flexC">宠物问诊-兽医端</div> |
30 | <div className="subtitle">账号</div> | 71 | <div className="subtitle">账号</div> |
31 | - <Input placeholder="请输入手机号" className="inputbox" /> | 72 | + <Input placeholder="请输入手机号" className="inputbox" value={form.phoneNum} onChange={(e) => setForm({ ...form, phoneNum: e.target.value })} /> |
32 | <div className="subtitle">验证码</div> | 73 | <div className="subtitle">验证码</div> |
33 | - <Input placeholder="请输入密码" className="inputbox" suffix={<div style={{ color: '#05b8d2', fontSize: '17px' }}>获取验证码</div>} /> | 74 | + <Input |
75 | + placeholder="请输入密码" | ||
76 | + className="inputbox" | ||
77 | + value={form.code} | ||
78 | + onChange={(e) => setForm({ ...form, code: e.target.value })} | ||
79 | + suffix={ | ||
80 | + <div style={{ color: '#05b8d2', fontSize: '17px', cursor: 'pointer' }} onClick={sendCodeHandler}> | ||
81 | + {countdown === 60 ? '获取验证码' : `${countdown}秒后可重新获取`} | ||
82 | + </div> | ||
83 | + } | ||
84 | + /> | ||
34 | <Button className="loginbtn" onClick={loginHandler} loading={loadingState}> | 85 | <Button className="loginbtn" onClick={loginHandler} loading={loadingState}> |
35 | 登录 | 86 | 登录 |
36 | </Button> | 87 | </Button> |
-
请 注册 或 登录 后发表评论