开药门诊模块 #开药门诊-需求分析 #理解开药门诊阶段流程分析
开药门诊-表单页面路由搭建 #步骤
准备基础结构代码路由配置首页跳转cv静态结构基础结构代码views/Consult/ConsultMedicine.vue
vue
consult-medicine
12345678910路由配置router/index.ts
ts{
path: '/consult/medicine',
component: () => import('@/views/Consult/ConsultMedicine.vue'),
meta: { title: '开药门诊' }
}
12345首页跳转Home/index.vue
vue to="/consult/medicine" class="nav" @click="store.setType(ConsultType.Medication)" > 开药门诊
线上买药更方便
123456789静态结构ConsultMedicine.vue
vue
title="开药门诊" right-text="问诊记录" @click-right="$router.push('/user/consult')" >
type="textarea" rows="3" placeholder="请输入所患疾病名称" >
肝功能
肾功能
过敏史
生育状态及计划
.consult-medicine-page {
padding-top: 46px;
.van-button {
font-size: 16px;
margin-bottom: 30px;
&.disabled {
opacity: 1;
background: #fafafa;
color: #d9dbde;
border: #fafafa;
}
}
.illness-form {
padding: 0 15px 15px 15px;
.adm-list-header {
padding-bottom: 5px;
font-size: 16px;
font-weight: 500;
color: #121826;
border-bottom: none;
margin-top: 30px;
}
.van-field {
padding: 0;
&::after {
border-bottom: none;
}
}
.item {
> p {
color: var(--cp-text3);
padding: 15px 0;
}
}
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778开药门诊-表单单选框处理 #需求:
完成表单双向数据绑定
步骤:
定义枚举定义选项options开药门诊页面中使用cp-radio-btn扩展Consult类型添加字段,store中添加方法保存字段双向数据绑定定义枚举enums/index.ts
ts// 肝功能
export enum LiverFunction {
/** 正常 */
Normal,
/** 异常 */
Abnormal,
/** 不清楚 */
Unclear
}
// 肾功能
export enum RenalFunction {
/** 正常 */
Normal,
/** 异常 */
Abnormal,
/** 不清楚 */
Unclear
}
// 过敏史
export enum AllergicHistory {
/** 正常 */
Normal,
/** 异常 */
Abnormal,
/** 不清楚 */
Unclear
}
// 生育状态及计划
export enum FertilityStatus {
/** 无 */
No,
/** 备孕中 */
TryingToConceive,
/** 已怀孕 */
AlreadyPregnant,
/** 哺乳期 */
Breastfeeding
}
1234567891011121314151617181920212223242526272829303132333435363738定义options选项services/constant
ts// 肝功能
export const liverFunctionOptions = [
{ label: '正常', value: LiverFunction.Normal },
{ label: '异常', value: LiverFunction.Abnormal },
{ label: '不清楚', value: LiverFunction.Unclear }
]
// 肾功能
export const renalFunctionOptions = [
{ label: '正常', value: RenalFunction.Normal },
{ label: '异常', value: RenalFunction.Abnormal },
{ label: '不清楚', value: RenalFunction.Unclear }
]
// 过敏史
export const allergicHistoryOptions = [
{ label: '正常', value: AllergicHistory.Normal },
{ label: '异常', value: AllergicHistory.Abnormal },
{ label: '不清楚', value: AllergicHistory.Unclear }
]
// 生育状态及计划
export const fertilityStatusOptions = [
{ label: '无', value: FertilityStatus.No },
{ label: '备孕中', value: FertilityStatus.TryingToConceive },
{ label: '已怀孕', value: FertilityStatus.AlreadyPregnant },
{ label: '哺乳期中', value: FertilityStatus.Breastfeeding }
]
1234567891011121314151617181920212223242526使用cp-radio-btn在开药门诊页面中ConsultMedicine.vue
tsimport {
liverFunctionOptions,
allergicHistoryOptions,
fertilityStatusOptions,
renalFunctionOptions
} from '@/services/constants'
123456vue
肝功能
肾功能
过敏史
生育状态及计划
12345678910111213141516扩展Consult类型添加字段,store中添加方法保存字段
consult.d.ts
tsimport type {
ConsultType,
IllnessTime,
OrderType,
LiverFunction,
RenalFunction,
AllergicHistory,
FertilityStatus
} from '@/enums'
...
export type Consult = {
...
/** 肝功能 */
liverFunction: LiverFunction
/** 肾功能 */
renalFunction: RenalFunction
/** 过敏史 */
allergicHistory: AllergicHistory
/** 生育状态及计划 */
fertilityStatus: FertilityStatus
}
export type MedicineIllness = Pick<
PartialConsult,
| 'illnessDesc'
| 'liverFunction'
| 'renalFunction'
| 'allergicHistory'
| 'fertilityStatus'
| 'pictures'
>
123456789101112131415161718192021222324252627282930313233src/stores/modules/consult.ts
tsimport type { ConsultIllness, PartialConsult, MedicineIllness } from '@/types/consult'
...
// 记录问药门诊病情
const setMedicineIlness = (illness: MedicineIllness) => {
consult.value.illnessDesc = illness.illnessDesc
consult.value.liverFunction = illness.liverFunction
consult.value.renalFunction = illness.renalFunction
consult.value.allergicHistory = illness.allergicHistory
consult.value.fertilityStatus = illness.fertilityStatus
consult.value.pictures = illness.pictures
}
return {
...,
setMedicineIlness
}
1234567891011121314151617双向数据绑定ConsultMedicine.vue
tsimport type { MedicineIllness } from '@/types/consult'
import { ref } from 'vue'
const form = ref
illnessDesc: '',
liverFunction: undefined,
renalFunction: undefined,
allergicHistory: undefined,
fertilityStatus: undefined,
pictures: []
})
12345678910vue
type="textarea" rows="3" placeholder="请输入所患疾病名称" v-model="form.illnessDesc" >
肝功能
:options="liverFunctionOptions" v-model="form.liverFunction" >
肾功能
:options="renalFunctionOptions" v-model="form.renalFunction" >
过敏史
:options="allergicHistoryOptions" v-model="form.allergicHistory" >
生育状态及计划
:options="fertilityStatusOptions" v-model="form.fertilityStatus" >
12345678910111213141516171819202122232425262728293031323334353637383940414243开药门诊-封装上传组件 #极速问诊中图文问诊用到了上传图片功能,在开药门诊中我们也有上传图片功能,所以需要封装上传组件
步骤:
封装组件CpUpload为CpUpload组件提供类型ConsultIllness使用上传组件ConsultMedicine使用上传组件封装组件components/CpUpload.vue
vue
import { uploadImage } from '@/services/consult'
import type { UploaderFileListItem } from 'vant'
import type { UploaderAfterRead } from 'vant/lib/uploader/types'
import { ref } from 'vue'
import type { Image } from '@/types/consult'
const emit = defineEmits<{
(e: 'uploadSuccess', img: Image): void
(e: 'deleteSuccess', item: UploaderFileListItem): void
}>()
// 上传图片
const fileList = ref
// 图片上传
const onAfterRead: UploaderAfterRead = (item) => {
if (Array.isArray(item)) return
if (!item.file) return
item.status = 'uploading'
item.message = '上传中...'
uploadImage(item.file)
.then((res) => {
item.status = 'done'
item.message = undefined
item.url = res.data.url
emit('uploadSuccess', res.data)
})
.catch(() => {
item.status = 'failed'
item.message = '上传失败'
})
}
const onDeleteImg = (item: UploaderFileListItem) => {
emit('deleteSuccess', item)
}
const setFileList = (val: Image[]) => {
fileList.value = val
}
defineExpose({
setFileList
})
upload-icon="photo-o" upload-text="上传图片" max-count="9" :max-size="5 * 1024 * 1024" v-model="fileList" :after-read="onAfterRead" @delete="onDeleteImg" >
上传内容仅医生可见,最多9张图,最大5MB
.illness-img {
padding-top: 16px;
margin-bottom: 40px;
display: flex;
align-items: center;
.tip {
font-size: 12px;
color: var(--cp-tip);
}
::v-deep() {
.van-uploader {
&__preview {
&-delete {
left: -6px;
top: -6px;
border-radius: 50%;
background-color: var(--cp-primary);
width: 20px;
height: 20px;
&-icon {
transform: scale(0.9) translate(-22%, 22%);
}
}
&-image {
border-radius: 8px;
overflow: hidden;
}
}
&__upload {
border-radius: 8px;
}
&__upload-icon {
color: var(--cp-text3);
}
}
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101为CpUpload组件提供类型src/types/components.d.ts
ts...
import CpUpload from '@/components/CpUpload.vue'
declare module 'vue' {
interface GlobalComponents {
// 添加组件类型
...
CpUpload: typeof CpUpload
}
}
1234567891011ConsultIllness.vue使用上传组件
ts// 数据的回显
onMounted(() => {
if (store.consult.illnessDesc) {
showConfirmDialog({
title: '温馨提示',
message: '是否恢复之前填写的病情信息?',
closeOnPopstate: false
}).then(() => {
// 回显数据
const { illnessDesc, illnessTime, consultFlag, pictures } = store.consult
form.value = { illnessDesc, illnessTime, consultFlag, pictures }
cpUploadRef.value.setFileList(pictures || [])
})
}
})
const cpUploadRef = ref()
const onUploadSuccess = (image: Image) => {
form.value.pictures?.push(image)
}
const onDeleteSuccess = (item: UploaderFileListItem) => {
form.value.pictures = form.value.pictures?.filter(
(pic) => pic.url !== item.url
)
}
12345678910111213141516171819202122232425vue ref="cpUploadRef" @upload-success="onUploadSuccess" @delete-success="onDeleteSuccess" >
12345ConsultMedicine.vue使用上传组件
tsconst cpUploadRef = ref()
const onUploadSuccess = (image: Image) => {
form.value.pictures?.push(image)
}
const onDeleteSuccess = (item: UploaderFileListItem) => {
form.value.pictures = form.value.pictures?.filter(
(pic) => pic.url !== item.url
)
}
123456789vue ref="cpUploadRef" @upload-success="onUploadSuccess" @delete-success="onDeleteSuccess" >
12345开药门诊-表单页面下一步功能 #ConsultMedicine
步骤:
控制按钮样式校验逻辑下一步跳转逻辑控制按钮样式
tsconst disabled = computed(
() =>
!form.value.illnessDesc ||
form.value.liverFunction === undefined ||
form.value.renalFunction === undefined ||
form.value.allergicHistory === undefined ||
form.value.fertilityStatus === undefined
)
12345678vue :class="{ disabled }" type="primary" block round @click="next" > 下一步
123456789校验逻辑
tsconst next = () => {
if (!form.value.illnessDesc) return showToast('请输入病情描述')
if (form.value.liverFunction === undefined)
return showToast('请选择肝功能情况')
if (form.value.renalFunction === undefined)
return showToast('请选择肾功能情况')
if (form.value.allergicHistory === undefined)
return showToast('请选择过敏史情况')
if (form.value.fertilityStatus === undefined)
return showToast('请选择生育状态及计划')
}
123456789101112下一步跳转逻辑
tsconst store = useConsultStore()
const router = useRouter()
const next = () => {
....
// 记录病情
store.setMedicineIlness(form.value)
// 跳转,携带标识
router.push('/user/patient?isChange=1&from=medicineConsult')
}
123456789开药门诊-选择患者页面下一步功能 #步骤:
开药门诊选择患者下一步跳转选择药品,根据标识跳转新建页面结构路由规则配置开药门诊选择患者下一步跳转选择药品,根据标识跳转views/User/PatientPage.vue
tsconst fromMedicineConsultFlag = computed(
() => route.query.from === 'medicineConsult'
)
const next = () => {
if (!patientId.value) return showToast('请选择患者')
store.setPatient(patientId.value)
if (fromMedicineConsultFlag.value) {
router.push('/consult/choose')
} else {
router.push('/consult/pay')
}
}
123456789101112新建页面结构ConsultChoose.vue
vue
consult-choose
12345678910路由规则配置router/index.ts
ts{
path: '/consult/choose',
component: () => import('@/views/Consult/ConsultChoose.vue'),
meta: { title: '选择药品' }
}
12345选择药品-页面结构 #步骤:
静态结构步进器组件学习及样式处理静态结构ConsultChoose.vue
vue
import { showToast } from 'vant'
import { ref } from 'vue'
const searchValue = ref('')
const onSearch = (val: string) => showToast(val)
const onCancel = () => showToast('取消')
const step = ref(1)
v-model="searchValue" show-action placeholder="搜一搜: 药品名称" @search="onSearch" @cancel="onCancel" /> 优赛明 维生素E乳 {{ i }}
80ml ¥25.00
.consult-choose-page {
padding-top: 46px;
.van-search {
position: sticky;
top: 46px;
z-index: 10;
background-color: #fff;
}
.van-action-bar {
border-top: 1px solid rgba(237, 237, 237, 0.9);
.total-price {
width: 200px;
font-size: 24px;
line-height: 18px;
font-weight: 700;
color: #121826;
}
}
.medicine-list {
background-color: #fff;
padding: 0 15px 45px;
.item {
display: flex;
flex-wrap: wrap;
padding: 15px 0;
.img {
width: 80px;
height: 70px;
border-radius: 2px;
overflow: hidden;
}
.info {
padding-left: 15px;
width: 250px;
.name {
display: flex;
font-size: 15px;
margin-bottom: 5px;
> span:first-child {
// width: 200px;
// width: 300px;
width: 40vw;
}
> span:last-child {
// width: 50px;
text-align: right;
}
}
.size {
margin-bottom: 5px;
.van-tag {
background-color: var(--cp-primary);
vertical-align: middle;
}
span:not(.van-tag) {
margin-left: 10px;
color: var(--cp-tag);
vertical-align: middle;
}
}
.price {
font-size: 16px;
color: #eb5757;
}
}
.desc {
width: 100%;
background-color: var(--cp-bg);
border-radius: 4px;
margin-top: 10px;
padding: 4px 10px;
color: var(--cp-tip);
}
}
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127步进器组件学习及样式处理
tsconst step = ref(0)
1vue v-model="step" min="0" :class="{ hide: step === 0 }" /> 12345scss.van-stepper { position: absolute; right: 0; bottom: 15px; :deep() { .van-stepper__input { background: none; } .van-stepper__minus { background-color: #fff; border: 0.5px solid #16c2a3; } .van-stepper__plus { background-color: #eaf8f6; } .van-stepper__minus, .van-stepper__plus { width: 20px; height: 20px; } } &.hide { :deep() { .van-stepper__minus, .van-stepper__input { visibility: hidden; } } } } 123456789101112131415161718192021222324252627282930选择药品-抽离列表卡片组件 #步骤: 抽离卡片组件抽离列表组件使用列表组件抽离卡片组件views/Consult/components/MedicineCard.vue vue import { ref } from 'vue' const step = ref(0) 优赛明 维生素E乳
80ml ¥25.00
.item {
display: flex;
padding: 15px 0;
.img {
width: 80px;
height: 70px;
border-radius: 2px;
overflow: hidden;
}
.info {
padding-left: 15px;
width: 250px;
.name {
display: flex;
font-size: 15px;
margin-bottom: 5px;
> span:first-child {
// width: 200px;
// width: 300px;
width: 40vw;
}
> span:last-child {
// width: 50px;
text-align: right;
}
}
.size {
margin-bottom: 5px;
.van-tag {
background-color: var(--cp-primary);
vertical-align: middle;
}
span:not(.van-tag) {
margin-left: 10px;
color: var(--cp-tag);
vertical-align: middle;
}
}
.price {
font-size: 16px;
color: #eb5757;
}
}
.desc {
width: 100%;
background-color: var(--cp-bg);
border-radius: 4px;
margin-top: 10px;
padding: 4px 10px;
color: var(--cp-tip);
}
}
.van-stepper {
position: absolute;
right: 0;
bottom: 15px;
:deep() {
.van-stepper__input {
background: none;
}
.van-stepper__minus {
background-color: #fff;
border: 0.5px solid #16c2a3;
}
.van-stepper__plus {
background-color: #eaf8f6;
}
.van-stepper__minus,
.van-stepper__plus {
width: 20px;
height: 20px;
}
}
&.hide {
:deep() {
.van-stepper__minus,
.van-stepper__input {
visibility: hidden;
}
}
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111抽离列表组件src/views/Consult/components/MedicineList.vue
vue
import MedicineCard from './MedicineCard.vue'
.medicine-list {
background-color: #fff;
padding: 0 15px 45px;
}
1234567891011121314151617使用列表组件ConsultChoose.vue
vueimport MedicineList from './components/MedicineList.vue'
...
...
12345选择药品-list组件渲染药品列表 #步骤:
van-list初步使用封装并调用请求渲染药品卡片van-list初步使用MedicineList.vue
vue
import { ref } from 'vue'
import MedicineCard from './MedicineCard.vue'
const list = ref
const loading = ref(false)
const finished = ref(false)
const onLoad = () => {
// 异步更新数据
// setTimeout 仅做示例,真实场景中一般为 ajax 请求
setTimeout(() => {
for (let i = 0; i < 10; i++) {
list.value.push(list.value.length + 1)
}
// 加载状态结束
loading.value = false
// 数据全部加载完成
if (list.value.length >= 40) {
finished.value = true
}
}, 1000)
}
v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" >
.medicine-list {
background-color: #fff;
padding: 0 15px 45px;
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647封装并调用请求
consult.d.ts编写类型,提取BasePage泛型别名
tsimport type { Medical } from './room'
// 药品列表查询参数
export type MedicineParams = PageParams & {
keyword: string
}
export type BasePage
pageTotal: number
total: number
rows: T
}
// 医生列表
export type DoctorList = Doctor[]
// 医生分页
export type DoctorPage = BasePage
// 文章列表
export type KnowledgeList = Knowledge[]
// 文章列表带分页
export type KnowledgePage = BasePage
// 药品列表
export type MedicineList = Medical[]
// 药品列表带分页
export type MedicinePage = BasePage
12345678910111213141516171819202122232425262728293031封装接口service/consult.ts
tsexport const getMedicinePage = (params: MedicineParams) => {
return request
}
123调用接口MedicineList.vue
vue
import { ref } from 'vue'
import MedicineCard from './MedicineCard.vue'
import type { MedicineList, MedicineParams } from '@/types/consult'
import { getMedicinePage } from '@/services/consult'
const list = ref
const loading = ref(false)
const finished = ref(false)
const params = ref
keyword: '',
pageSize: 10,
current: 1
})
const onLoad = async () => {
const { data } = await getMedicinePage(params.value)
list.value.push(...data.rows)
loading.value = false
if (params.value.current >= data.pageTotal) {
finished.value = true
} else {
finished.value = false
params.value.current++
}
}
v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" >
.medicine-list {
background-color: #fff;
padding: 0 15px 45px;
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748渲染药品卡片
父传子MedicineList.vue
vue v-for="item in list" :key="item.id" :item="item" >
12345子组件接收数据并渲染MedicineCard
vue
import type { Medical } from '@/types/room'
import { ref } from 'vue'
defineProps<{
item: Medical
}>()
const step = ref(0)
{{ item.name }}
{{ item.specs }}
¥{{ item.amount }}
123456789101112131415161718192021222324252627282930选择药品-搜索功能 #步骤:
搜索框双向数据绑定字段searchValue,keyword查询参数字段,回车后赋值该字段父传子监听字段,渲染页面ConsultChoose搜索框双向数据绑定字段searchValue,keyword查询参数字段,回车后赋值该字段
tsconst searchValue = ref('')
const keyword = ref('')
const onSearch = () => {
keyword.value = searchValue.value
}
const onCancel = () => {
keyword.value = ''
}
12345678父传子ConsultChoose
vue
12监听字段,重新渲染页面ConsultList
vueconst props = defineProps<{
keyword: string
}>()
const params = ref
keyword: props.keyword || '',
pageSize: 10,
current: 1
})
watch(
() => props.keyword,
(val) => {
list.value = []
params.value.keyword = val
params.value.current = 1
onLoad()
}
)
12345678910111213141516171819选择药品-购物车底部操作栏 #步骤:
Consult类型新增medicines字段store新增方法,存medicines字段处理药品增加数量减少数量逻辑计算属性处理数量和总金额Consult类型新增medicines字段
consult.d.ts
tsexport type Consult = {
...
/** 药品 */
medicines: Medical[]
}
12345store新增方法,存medicines字段
tsconst setMedicines = (val: Medical[]) => (consult.value.medicines = val)
return {
...
setMedicines
...
}
123456处理药品增加数量减少数量逻辑 MedicineCard
vue
import { useConsultStore } from '@/stores'
import type { Medical } from '@/types/room'
import { ref, watch } from 'vue'
const props = defineProps<{
item: Medical
}>()
const step = ref(0)
const consultStore = useConsultStore()
const onChange = (value: string, detail: { name: string }) => {
// 不要忘记在组件上name属性绑定id值
const medicines = consultStore.consult.medicines || []
const medicine = medicines?.find((item) => item.id === detail.name)
if (medicine) {
medicine.quantity = value
} else {
medicines.push({
...props.item,
quantity: value
})
}
consultStore.setMedicines(medicines.filter((item) => +item.quantity > 0))
}
watch(
() =>
consultStore.consult.medicines?.find((item) => item.id === props.item.id),
(val) => {
if (val) {
step.value = +val.quantity
} else {
step.value = 0
}
},
{ deep: true, immediate: true }
)
{{ item.name }}
:name="item.id" v-model="step" min="0" :class="{ hide: step === 0 }" @change="onChange" />
{{ item.specs }}
¥{{ item.amount }}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465计算属性处理数量和总金额 ConsultChoose
vue
....
const consultStore = useConsultStore()
const totalPrice = computed(() => {
return consultStore.consult.medicines
?.reduce((sum, item) => {
return (sum += +item.amount * +item.quantity)
}, 0)
.toFixed(2)
})
const cartLength = computed(
() =>
consultStore.consult.medicines?.length || 0
)
v-model="searchValue" show-action placeholder="搜一搜: 药品名称" @search="onSearch" @cancel="onCancel" /> icon="cart-o" :color="cartLength > 0 ? '#323233' : '#eee'" :badge="cartLength === 0 ? '' : cartLength" />
12345678910111213141516171819202122232425262728293031323334353637383940选择药品-购物车抽屉 #步骤:
抽屉结构样式打开抽屉,清空药品方法定义药品列表渲染实现增加减少清空药品功能抽屉结构样式ConsultChoose.vue
vue
药品清单共{{ cartLength }}件商品
清空
icon="cart-o" :color="cartLength > 0 ? '#323233' : '#eee'" :badge="cartLength === 0 ? '' : cartLength" />
12345678910111213141516171819202122scss .content {
--content-height: 400px;
--content-header-height: 25px;
padding: 16px;
height: var(--content-height);
.content-header {
position: sticky;
top: 0px;
z-index: 10;
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
height: var(--content-header-height);
padding-bottom: 10px;
&-left {
span {
font-size: 16px;
color: #000000;
margin-right: 10px;
}
span + span {
font-size: 13px;
color: var(--cp-primary);
}
}
&-right {
span {
margin-left: 5px;
}
}
}
.medicine-list {
padding-bottom: 45px;
}
}
123456789101112131415161718192021222324252627282930313233343536打开抽屉,清空药品方法定义
tsconst show = ref(false)
// 底部操作栏注册事件,注意不是抽屉内容的底部操作栏
const openCart = () => {
if (cartLength.value === 0) return showToast('请选择药品')
show.value = true
}
const clear = () => {
// console.log('clear')
show.value = false
}
1234567891011药品列表渲染
vueimport MedicineCard from './components/MedicineCard.vue'
v-for="item in consultStore.consult.medicines" :key="item.id" :item="item" >
12345678910实现增加减少清空药品功能
增加减少药品功能 MedicineCard.vue
vuewatch(
() => consultStore.consult.medicines?.find((item) => item.id === props.item.id),
(val) => {
if (val) {
step.value = +val.quantity
} else {
step.value = 0
}
},
{ deep: true, immediate: true }
)
1234567891011清空购物车ConsultChoose
tsconst clear = () => {
// console.log('clear')
consultStore.setMedicines([])
show.value = false
}
12345药品详情-跳转详情页 #步骤:
基本结构,路由配置跳转方法调用静态结构基本结构ConsultMedicineDetail
vue
medicine-detail
12345678910路由配置router/index.ts
ts{
path: '/medicineDetail/:id',
component: () => import('@/views/Consult/ConsultMedicineDetail.vue'),
meta: { title: '药品详情' }
},
12345跳转方法调用MedicineCard.vue
vue
{{ item.name }}
:name="item.id" v-model="step" min="0" :class="{ hide: step === 0 }" @change="onChange" @click.stop />
{{ item.specs }}
¥{{ item.amount }}
1234567891011121314151617181920212223静态结构ConsultMedicineDetail.vue
vue

双蚁祛湿通络胶囊(双蚁)
每粒装0.4克
¥186.00
补肝肾、益气血、活血通络、祛风除湿的功能,临床用于改善肝肾两亏、气血不足引起的腰膝冷痛、肢气肿胀、麻木不仁、酸软乏力、屈伸不利等痹证症状。
.medicine-detail-page {
padding: 46px 0px 45px;
.van-swipe {
img {
width: 100%;
}
}
.detail-top {
padding: 15px;
.info {
padding-left: 15px;
width: 250px;
.name {
display: flex;
font-size: 15px;
margin-bottom: 5px;
> span:first-child {
width: auto;
}
> span:last-child {
text-align: right;
}
}
.size {
margin-bottom: 5px;
.van-tag {
background-color: var(--cp-primary);
vertical-align: middle;
}
span:not(.van-tag) {
margin-left: 10px;
color: var(--cp-tag);
vertical-align: middle;
}
}
.price {
font-size: 16px;
color: #eb5757;
}
}
}
.detail-bottom {
padding: 15px;
.info-item {
margin-bottom: 30px;
.info-title {
font-size: 18px;
font-family: PingFang SC, PingFang SC-Medium;
font-weight: 500;
color: #000000;
}
.info-desc {
margin-top: 10px;
font-size: 14px;
font-family: PingFang SC, PingFang SC-Regular;
font-weight: 400;
color: #3c3e42;
}
}
}
.pay-space {
height: 12px;
background-color: var(--cp-bg);
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146药品详情-渲染页面 #步骤:
定义类型封装接口调用请求渲染定义类型consult.d.ts
tsexport type MedicineDetail = Medical & {
/** 品牌 */
brand: string
brandId?: any
/** 生产企业 */
manufacturer: string
/** 批准文号 */
approvalNo: string
/** 有效期 */
expiration: string
classify: string
classifyId: string
/** 功能主治 */
indicationsFunction: string
/** 禁忌 */
contraindication: string
/** 不良反应 */
untowardReaction: string
/** 注意事项 */
preparation: string
/** 执行标准 */
standard: string
/** 药品图片 */
mainPictures: string[]
creator: string
updator: string
createTime: string
updateTime: string
deleteState: number
}
12345678910111213141516171819202122232425262728293031封装接口services/consult.ts
tsexport const getMedicineDetail = (id: string) => {
return request
}
123调用请求渲染ConsultMedicineDetail.vue
vue
import { getMedicineDetail } from '@/services/consult'
import type { MedicineDetail } from '@/types/consult'
import { ref } from 'vue'
import { onMounted } from 'vue'
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const id = computed(() => route.params.id)
const detail = ref
onMounted(() => {
loadDetail()
})
const loadDetail = async () => {
const { data } = await getMedicineDetail(id.value as string)
detail.value = data
}
{{ detail.name }}
{{ detail.specs }}
¥{{ detail.amount }}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889药品详情-底部操作栏功能 #列表页和详情页都有底部操作栏,所以我们可以抽离组件
步骤:
封装底部操作栏组件区分加入药箱逻辑加入药箱逻辑实现申请开单跳转问诊室封装底部操作栏组件MedicineAction.vue
vue
import { useConsultStore } from '@/stores'
import { computed, ref } from 'vue'
import MedicineCard from './MedicineCard.vue'
const consultStore = useConsultStore()
const totalPrice = computed(() => {
return consultStore.consult.medicines
?.reduce((sum, item) => {
return (sum += +item.amount * +item.quantity)
}, 0)
.toFixed(2)
})
const cartLength = computed(() => consultStore.consult.medicines?.length || 0)
const show = ref(false)
const openCart = () => {
if (cartLength.value === 0) return
show.value = true
}
const clear = () => {
// console.log('clear')
consultStore.setMedicines([])
show.value = false
}
icon="cart-o" :badge="cartLength === 0 ? '' : cartLength" :color="cartLength > 0 ? '#323233' : '#eee'" @click="openCart" />
药品清单共{{ cartLength }}件商品
清空
v-for="item in consultStore.consult.medicines" :key="item.id" :item="item" >
icon="cart-o" :color="cartLength > 0 ? '#323233' : '#eee'" :badge="cartLength === 0 ? '' : cartLength" />
.van-action-bar {
border-top: 1px solid rgba(237, 237, 237, 0.9);
.total-price {
width: 200px;
font-size: 24px;
line-height: 18px;
font-weight: 700;
color: #121826;
}
}
.content {
--content-height: 400px;
--content-header-height: 25px;
padding: 16px;
height: var(--content-height);
.content-header {
position: sticky;
top: 0px;
z-index: 10;
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
height: var(--content-header-height);
padding-bottom: 10px;
&-left {
span {
font-size: 16px;
color: #000000;
margin-right: 10px;
}
span + span {
font-size: 13px;
color: var(--cp-primary);
}
}
&-right {
span {
margin-left: 5px;
}
}
}
.medicine-list {
padding-bottom: 45px;
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121ConsultMedicineDetail使用底部操作栏
vue
1区分加入药箱逻辑MedicineAction
ts
...
withDefaults(
defineProps<{
from?: 'list' | 'detail'
}>(),
{
from: 'list'
}
)
const onAskDocotor = () => {
console.log('申请开方')
}
const onAddToCart = () => {
console.log('加入药箱')
}
123456789101112131415161718192021底部操作栏按钮区分,注意抽屉内容都是申请开方
vue v-if="from === 'list'" type="primary" text="申请开方" @click="onAskDocotor" /> v-else type="primary" text="加入药箱" @click="onAddToCart" > 123456789101112加入药箱逻辑实现,子传父通知父组件处理数据 MedicineAction tsconst emits = defineEmits<{ (e: 'addToCart'): void }>() const onAddToCart = () => { emits('addToCart') } 1234567ConsultMedicineDetail ts// ConsultMedicineDetail const consultStore = useConsultStore() const onAddToCart = () => { const medicines = consultStore.consult.medicines || [] const medicine = medicines?.find((item) => item.id === detail.value?.id) if (medicine) { medicine.quantity += 1 } else { medicines.push({ amount: detail.value?.amount!, avatar: detail.value?.avatar!, id: detail.value?.id!, name: detail.value?.name!, prescriptionFlag: detail.value?.prescriptionFlag!, specs: detail.value?.specs!, usageDosag: detail.value?.usageDosag!, quantity: '1' }) } consultStore.setMedicines(medicines) } 123456789101112131415161718192021申请开单跳转问诊室,需要先生成订单id,订单id的参数,我们都存在pinia中,由于不同主线需要的参数不同,所以,我们要区分下 utils/createOrderParams ts// utils/createOrderParams import { ConsultType } from '@/enums' import type { PartialConsult } from '@/types/consult' type Key = keyof PartialConsult // 三条线都有的字段 export const commonKeys: Key[] = [ 'type', // 问医生,极速问诊,开药门诊 'illnessDesc', // 病情描述 'patientId', // 患者Id 'pictures' // 图片 ] // 极速问诊字段 export const fastKeys: Key[] = [ ...commonKeys, 'depId', // 科室 'illnessTime', // 患病时长 'consultFlag' // 是否就诊过 ] // 问医生字段,比极速问诊多一个docId 医生Id // export const doctorKeys: Key[] = [...fastKeys, 'docId'] // 开药门诊字段 export const medicineKeys: Key[] = [ ...commonKeys, 'allergicHistory', // 过敏史 'fertilityStatus', // 生育状态及计划 'liverFunction', // 肝功能 'renalFunction', // 肾功能 'medicines' // 药品 ] export const getCreateOrderParams = ( consult: PartialConsult, type: ConsultType = ConsultType.Fast ) => { const params: Record switch (type) { case ConsultType.Doctor: // for (const key of doctorKeys) { // params[key] = consult[key] // } break case ConsultType.Fast: for (const key of fastKeys) { params[key] = consult[key] } break case ConsultType.Medication: for (const key of medicineKeys) { params[key] = consult[key] } break } return params } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960此时再处理申请开单逻辑 MedicineAction ts// MedicineAction const onAskDocotor = async () => { const medicines = consultStore.consult.medicines || [] if (medicines?.length === 0) return showToast('请先选药') const params = getCreateOrderParams( consultStore.consult, ConsultType.Medication ) // console.log(params) try { const { data } = await createConsultOrder(params) router.push(`/room?orderId=${data.id}&from=medicine`) } catch (e) { return showDialog({ title: '温馨提示', message: '问诊信息不完整请重新填写', closeOnPopstate: false }).then(() => { router.push('/') }) } finally { consultStore.clear() } } 123456789101112131415161718192021222324问诊室-开药门诊信息渲染 #步骤: 渲染开药门诊信息数据转中文渲染开药门诊信息 RoomMessage ts// RoomMessage const route = useRoute() const fromPage = computed(() => route.query.from) 123vue 肝功能 {{ item.msg.consultRecord.liverFunction }} | 肾功能 {{ item.msg.consultRecord.renalFunction }} | 过敏史 {{ item.msg.consultRecord.allergicHistory }} | 生育状态 {{ item.msg.consultRecord.fertilityStatus }}
{{ getIllnessTimeText(item.msg.consultRecord?.illnessTime) }} | {{ getConsultFlagText(item.msg.consultRecord?.consultFlag) }} 12345678910vue {{ item.msg.consultRecord?.medicines .map( (item) => `${item.name} ${item.specs} X${item.quantity}` ) .join(',') }} 1234567891011数据转中文 filter.ts ts// filter.ts ... export const getLiverFunctionText = (val: LiverFunction) => { return liverFunctionOptions.find((item) => item.value === val)?.label } export const getAllergicHistoryText = (val: AllergicHistory) => { return allergicHistoryOptions.find((item) => item.value === val)?.label } export const getFertilityStatusText = (val: FertilityStatus) => { return fertilityStatusOptions.find((item) => item.value === val)?.label } export const getRenalFunctionText = (val: RenalFunction) => { return renalFunctionOptions.find((item) => item.value === val)?.label } 1234567891011121314151617181920vue 肝功能 {{ getLiverFunctionText(item.msg.consultRecord.liverFunction) }} | 肾功能 {{ getRenalFunctionText(item.msg.consultRecord.renalFunction) }} | 过敏史 {{ getAllergicHistoryText(item.msg.consultRecord.allergicHistory) }} | 生育状态 {{ getFertilityStatusText(item.msg.consultRecord.fertilityStatus) }} 12345678910