1. 开方2.0优化重构,线下接诊已完成

This commit is contained in:
李琦
2026-05-12 10:10:38 +08:00
parent 45996a26b6
commit e9966af14d
21 changed files with 3069 additions and 3208 deletions

View File

@@ -20,7 +20,7 @@ export function getAcceptingPatientList(data) {
}) })
} }
// 获取患者详情 // 获取患者详情(挂号维度的就诊信息,不含处方列表)
export function getPatientItem(id) { export function getPatientItem(id) {
return req.request({ return req.request({
url: '/newApi/doctor-reception-wx/patient-item', url: '/newApi/doctor-reception-wx/patient-item',
@@ -29,6 +29,33 @@ export function getPatientItem(id) {
}) })
} }
/** 本次挂号处方分页 */
export function getRegisterPrescriptionList(data) {
return req.request({
url: '/newApi/doctor-reception-wx/register-prescription-list',
method: 'GET',
data
})
}
/** 患者历史处方分页(可排除某次挂号) */
export function getPatientPrescriptionHistory(data) {
return req.request({
url: '/newApi/doctor-reception-wx/patient-prescription-history',
method: 'GET',
data
})
}
/** 撤回处方(待支付) */
export function withdrawPrescriptionWx(data) {
return req.request({
url: '/newApi/doctor-reception-wx/withdraw-prescription',
method: 'POST',
data
})
}
/** /**
* 根据患者ID获取患者信息 * 根据患者ID获取患者信息
* @param {number} patientId - 患者ID * @param {number} patientId - 患者ID
@@ -210,10 +237,10 @@ export function switchStoreApi(data) {
}) })
} }
// 获取处方详情 // 获取处方详情(医生小程序 doctor-reception-wx 路由,与接诊接口同鉴权)
export function getPrescriptionInfoApi(id) { export function getPrescriptionInfoApi(id) {
return req.request({ return req.request({
url: '/newApi/prescription/detail', url: '/newApi/doctor-reception-wx/prescription-detail',
method: 'GET', method: 'GET',
data: { id } data: { id }
}) })
@@ -273,6 +300,24 @@ export function saveChineseCommonPrescriptionApi(data) {
}) })
} }
// 保存颗粒药/保健食品常用方
export function saveGranularCommonPrescriptionApi(data) {
return req.request({
url: '/newApi/common-prescription/save-granular',
method: 'POST',
data
})
}
// 删除常用方
export function deleteCommonPrescriptionApi(data) {
return req.request({
url: '/newApi/common-prescription/delete',
method: 'POST',
data
})
}
// 获取公共医嘱列表 // 获取公共医嘱列表
export function getDoctorOrderCommonList(data) { export function getDoctorOrderCommonList(data) {
return req.request({ return req.request({

View File

@@ -20,10 +20,13 @@ const arr = [
'password' 'password'
] ]
// 敏感数据 // 敏感数据(解密后做脱敏;需与下方 switch 分支一致)
const sensitiveData = [ const sensitiveData = [
'id_card', 'id_card',
'idcard' 'idcard',
'mobile',
'express_mobile',
'patient_mobile'
] ]
/** /**
* 请求拦截器 * 请求拦截器
@@ -53,11 +56,17 @@ const resInterceptor = (response, conf = {}) => {
// 响应拦截器 // 响应拦截器
if (statusCode >= 200 && statusCode < 300) { if (statusCode >= 200 && statusCode < 300) {
_responseLog(response, conf, "response 200-299") _responseLog(response, conf, "response 200-299")
// 解密 // 解密Yii业务在 dataLaravel jok业务在 result
let res = response.data let res = response.data
if (res == null || typeof res !== 'object') {
return res
}
if (res.data != null) { if (res.data != null) {
res.data = getRes(res.data) res.data = getRes(res.data)
} }
if (res.result != null) {
res.result = getRes(res.result)
}
return res return res
} else if (statusCode === 500) { } else if (statusCode === 500) {
uni.showToast({ uni.showToast({
@@ -133,11 +142,14 @@ function _responseLog(res, conf = {}, describe = null) {
} }
function getRes(obj, isDecode = true) { function getRes(obj, isDecode = true) {
if (obj == null || typeof obj !== 'object') {
return obj
}
try { try {
for (const key in obj) { for (const key in obj) {
if (Array.isArray(obj[key])) { if (Array.isArray(obj[key])) {
obj[key] = getRes(obj[key]) obj[key] = getRes(obj[key])
} else if (typeof obj[key] === 'object') { } else if (typeof obj[key] === 'object' && obj[key] !== null) {
obj[key] = getRes(obj[key]) obj[key] = getRes(obj[key])
} else { } else {
// 判断obj[key]是否在arr中 // 判断obj[key]是否在arr中

View File

@@ -21,6 +21,20 @@ if (!String.prototype.padStart) {
} }
} }
/**
* iOS含部分微信小程序环境下 new Date('yyyy-MM-dd HH:mm:ss') 无效,需转为 ISO 类似格式
*/
function normalizeDateInputForIOS(dateTime) {
if (dateTime == null || dateTime === '') return dateTime
if (typeof dateTime === 'number') return dateTime
const s = String(dateTime).trim()
// "2025-03-06 17:34:03" / "2025-03-06 17:34:03.000" -> T 分隔
if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/.test(s)) {
return s.replace(' ', 'T')
}
return dateTime
}
// 其他更多是格式化有如下: // 其他更多是格式化有如下:
// yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 // yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合
function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd') { function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd') {
@@ -28,7 +42,11 @@ function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd') {
if (!dateTime) dateTime = Number(new Date()); if (!dateTime) dateTime = Number(new Date());
// 如果dateTime长度为10或者13则为秒和毫秒的时间戳如果超过13位则为其他的时间格式 // 如果dateTime长度为10或者13则为秒和毫秒的时间戳如果超过13位则为其他的时间格式
if (dateTime.toString().length == 10) dateTime *= 1000; if (dateTime.toString().length == 10) dateTime *= 1000;
dateTime = normalizeDateInputForIOS(dateTime);
let date = new Date(dateTime); let date = new Date(dateTime);
if (Number.isNaN(date.getTime())) {
return typeof dateTime === 'string' ? dateTime : ''
}
let ret; let ret;
let opt = { let opt = {
"y+": date.getFullYear().toString(), // 年 "y+": date.getFullYear().toString(), // 年

View File

@@ -7,10 +7,16 @@ import {
} from '@/common/js/util.js' } from '@/common/js/util.js'
// 引入uView主JS库 // 引入uView主JS库
import uView from "uview-ui"; import uView from "uview-ui";
import timeFormatIOSFix from '@/common/js/timeFormat.js'
import store from './store'; import store from './store';
import share from '@/common/share.js' import share from '@/common/share.js'
Vue.mixin(share) Vue.mixin(share)
Vue.use(uView); Vue.use(uView);
// 覆盖 uView 的 timeFormat / date修复 iOS 下 `yyyy-MM-dd HH:mm:ss` 无法解析
if (uni && uni.$u) {
uni.$u.timeFormat = timeFormatIOSFix
uni.$u.date = timeFormatIOSFix
}
let systemInfo = uni.getSystemInfoSync(); let systemInfo = uni.getSystemInfoSync();
const $d = { const $d = {
addStyle, addStyle,

View File

@@ -40,9 +40,9 @@
登录</u-button> 登录</u-button>
</view> </view>
<view class="register"> <!-- <view class="register">-->
<text @click="$go('../../subPackages/sub_workbench/workbench_identity')">手机号注册</text> <!-- <text @click="$go('../../subPackages/sub_workbench/workbench_identity')">手机号注册</text>-->
</view> <!-- </view>-->
</view> </view>
@@ -79,8 +79,8 @@
}, },
onLoad(e) { onLoad(e) {
const accountInfo = uni.getAccountInfoSync(); // const accountInfo = uni.getAccountInfoSync();
console.log(accountInfo.miniProgram.appId); // 小程序 appId // console.log('appid', accountInfo.miniProgram.appId); // 小程序 appId
}, },
mounted() { mounted() {
const token = uni.getStorageSync('token') const token = uni.getStorageSync('token')

View File

@@ -521,21 +521,23 @@
}, },
// 跳转到小程序 // 跳转到小程序
toNext() { toNext() {
uni.navigateToMiniProgram({
appId: 'wx8e8f04d0cf7831bf', this.$toast('开发中~');
path: 'pages/login/index?id=', // uni.navigateToMiniProgram({
envVersion: "release", // appId: 'wx8e8f04d0cf7831bf',
extraData: { // path: 'pages/login/index?id=',
'data1': 'test' // envVersion: "release",
}, // extraData: {
success: res => { // 'data1': 'test'
// 打开成功 // },
console.log("打开成功", res); // success: res => {
}, // // 打开成功
fail: err => { // console.log("打开成功", res);
console.log(err); // },
} // fail: err => {
}) // console.log(err);
// }
// })
}, },
// 获取正在接诊的患者列表 // 获取正在接诊的患者列表
getAcceptingPatientList() { getAcceptingPatientList() {

View File

@@ -24,10 +24,10 @@
<view @click="isDel = true" class="btn btn1">删除医嘱</view> <view @click="isDel = true" class="btn btn1">删除医嘱</view>
<view @click="showAdd" class="btn btn2">添加医嘱</view> <view @click="showAdd" class="btn btn2">添加医嘱</view>
</view> </view>
<!-- <view v-else class="btnBox">--> <view v-else class="btnBox">
<!-- <view @click="isDel = false; selectId = []" class="btn btn1">暂不删除</view>--> <view @click="isDel = false; selectId = []" class="btn btn1">暂不删除</view>
<!-- <view @click="templateDelete" class="btn btn2">删除医嘱</view>--> <view @click="templateDelete" class="btn btn2">删除医嘱</view>
<!-- </view>--> </view>
<view class="modal" v-if="modalVisible"> <view class="modal" v-if="modalVisible">
<view class="mask"></view> <view class="mask"></view>
<view class="main"> <view class="main">

View File

@@ -201,14 +201,13 @@
</view> </view>
</scroll-view> </scroll-view>
<!-- 底部按钮 --> <!-- 底部按钮另存常用方未对接保存接口不展示 -->
<view class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom"> <view class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
<view class="btnText" @click="saveAsCommonPrescription">另存常用方</view>
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="sendPrescription" @click="sendPrescription"
shape="circle" shape="circle"
:custom-style="{backgroundColor:buttonLodging?'#A5DCD2':'#6ACDBB',color:'#fff',height:'86rpx', width: '448rpx'}" :custom-style="{backgroundColor:buttonLodging?'#A5DCD2':'#6ACDBB',color:'#fff',height:'86rpx', width: '686rpx'}"
:disabled="buttonLodging"> :disabled="buttonLodging">
发送处方 发送处方
</u-button> </u-button>
@@ -708,18 +707,6 @@ export default {
} }
}, },
/**
* 另存常用方
*/
async saveAsCommonPrescription() {
if (this.currentDrugs.length === 0) {
uni.showToast({ title: '请先添加药品后再保存为常用方', icon: 'none' });
return;
}
uni.showToast({ title: '另存常用方功能待实现', icon: 'none' });
// TODO: 实现另存常用方功能
},
/** /**
* 返回 * 返回
*/ */

View File

@@ -1,129 +1,143 @@
<template> <template>
<view class="chinese-medicine-config"> <view class="chinese-medicine-config">
<view class="config-section"> <!-- 制剂方式 -->
<view class="section-title"> <view class="config-section m-b-32">
<d-text text="制剂方式" className="fs-3 main-c"></d-text> <view class="section-title m-b-16">
<d-text text="制剂方式" className="fs-30 font-bold color-title"></d-text>
</view> </view>
<view class="section-content"> <view class="section-content">
<view class="tag-group"> <view class="modern-pill-group">
<view <view
class="tag-item" class="modern-pill"
:class="{ active: localConfig.ruleType === 1 }" :class="{ active: localConfig.ruleType === 1 }"
@click="handleRuleTypeChange(1)" @click="handleRuleTypeChange(1)"
> >
自制剂 自制剂
</view> </view>
<view <view
class="tag-item" class="modern-pill"
:class="{ active: localConfig.ruleType === 2 }" :class="{ active: localConfig.ruleType === 2 }"
@click="handleRuleTypeChange(2)" @click="handleRuleTypeChange(2)"
> >
委托调剂 委托调剂
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 自制剂配置 --> <!-- 自制剂配置 -->
<view class="config-section" v-if="localConfig.ruleType === 1"> <view class="config-section border-t-dashed pt-32 m-b-32" v-if="localConfig.ruleType === 1">
<view class="section-title"> <view class="section-title m-b-16">
<d-text text="包法" className="fs-3 main-c"></d-text> <d-text text="包法选择" className="fs-30 font-bold color-title"></d-text>
</view> </view>
<view class="section-content"> <view class="section-content">
<view class="tag-group"> <view class="modern-pill-group">
<view <view
v-for="item in packageMethodList" v-for="item in packageMethodList"
:key="item.id" :key="item.id"
class="tag-item" class="modern-pill outline"
:class="{ active: localConfig.packageMethodId === item.id }" :class="{ active: localConfig.packageMethodId === item.id }"
@click="handlePackageMethodTagClick(item)" @click="handlePackageMethodTagClick(item)"
> >
{{ item.name }} {{ item.name }}
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 委托调剂配置 --> <!-- 委托调剂配置 -->
<view class="config-section" v-if="localConfig.ruleType === 2"> <view class="config-section border-t-dashed pt-32" v-if="localConfig.ruleType === 2">
<view class="section-title"> <view class="section-title m-b-16">
<d-text text="制剂" className="fs-3 main-c"></d-text> <d-text text="制剂要求" className="fs-30 font-bold color-title"></d-text>
</view> </view>
<view class="section-content"> <view class="section-content m-b-24">
<view class="tag-group"> <view class="modern-pill-group">
<view <view
v-for="item in processRuleList" v-for="item in processRuleList"
:key="item.id" :key="item.id"
class="tag-item" class="modern-pill outline"
:class="{ active: localConfig.processRuleId === item.id }" :class="{ active: localConfig.processRuleId === item.id }"
@click="handleProcessRuleTagClick(item)" @click="handleProcessRuleTagClick(item)"
> >
{{ item.name }} {{ item.name }}
</view> </view>
</view> </view>
</view> </view>
<view class="section-title m-t-16"> <view class="section-title m-b-16 m-t-24">
<d-text text="煎法" className="fs-3 main-c"></d-text> <d-text text="煎法说明" className="fs-30 font-bold color-title"></d-text>
</view> </view>
<view class="section-content"> <view class="section-content m-b-24">
<view class="tag-group"> <view class="modern-pill-group">
<view <view
v-for="item in childProcessRuleList" v-for="item in childProcessRuleList"
:key="item.id" :key="item.id"
class="tag-item" class="modern-pill outline"
:class="{ active: localConfig.childProcessRuleId === item.id }" :class="{ active: localConfig.childProcessRuleId === item.id }"
@click="handleChildProcessRuleTagClick(item)" @click="handleChildProcessRuleTagClick(item)"
> >
{{ item.name }} {{ item.name }}
</view> </view>
</view> </view>
</view> </view>
<view class="section-title m-t-16"> <view class="section-title m-b-16 m-t-24">
<d-text text="备注" className="fs-3 main-c"></d-text> <d-text text="附加备注" className="fs-30 font-bold color-title"></d-text>
</view> </view>
<view class="section-content"> <view class="section-content m-b-32">
<view class="tag-group"> <view class="modern-pill-group">
<view <view
v-for="item in processRuleNoteList" v-for="item in processRuleNoteList"
:key="item.id" :key="item.id"
class="tag-item" class="modern-pill outline"
:class="{ active: localConfig.processRuleNoteId === item.id }" :class="{ active: localConfig.processRuleNoteId === item.id }"
@click="handleProcessRuleNoteTagClick(item)" @click="handleProcessRuleNoteTagClick(item)"
> >
{{ item.note || item.name }} {{ item.note || item.name }}
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 用量和频次 --> <!-- 用量和频次 WesternUsageModal 保持一致的表单布局 -->
<view class="config-section"> <view class="config-form border-t-dashed pt-20">
<view class="section-title"> <view class="form-item flex-row flex-jus-sp flex-ali-center p-y-24">
<d-text text="用量" className="fs-3 main-c"></d-text> <view class="item-label">
<d-text text="剂数" className="fs-30 font-bold color-title"></d-text>
</view>
<view class="item-content flex-row flex-ali-center">
<u-number-box
v-model="localConfig.dosage"
:min="1"
:input-width="80"
:input-height="60"
bg-color="#F7F8FA"
/>
<d-text text="剂(天)" className="fs-28 color-sub m-l-16"></d-text>
</view>
</view> </view>
<view class="section-content">
<u-number-box v-model="localConfig.dosage" :min="1" /> <view class="form-item flex-row flex-jus-sp flex-ali-center p-y-24">
<d-text text="天" className="fs-3 color9 m-l-16"></d-text> <view class="item-label">
<u-input disabled class="input" placeholder="剂数" v-model="localConfig.dosage" :clearable="false" type="number" /> <d-text text="用药频次" className="fs-30 font-bold color-title"></d-text>
<d-text text="剂" className="fs-3 color9"></d-text> </view>
</view> <view class="item-content flex-row flex-ali-center">
</view> <u-number-box
v-model="localConfig.dayDosage"
<view class="config-section"> :min="1"
<view class="section-title"> :input-width="80"
<d-text text="频次" className="fs-3 main-c"></d-text> :input-height="60"
</view> bg-color="#F7F8FA"
<view class="section-content"> />
<u-number-box v-model="localConfig.dayDosage" :min="1" /> <d-text text="次/天" className="fs-28 color-sub m-l-16"></d-text>
<d-text text="次/天" className="fs-3 color9 m-l-16"></d-text> </view>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
// 保留原有逻辑
export default { export default {
name: 'ChineseMedicineConfig', name: 'ChineseMedicineConfig',
props: { props: {
@@ -211,7 +225,7 @@ export default {
this.localConfig.packageMethodId = null; this.localConfig.packageMethodId = null;
} }
}, },
/** /**
* 处理包法改变(标签点击) * 处理包法改变(标签点击)
* @param {Object} item - 包法项 * @param {Object} item - 包法项
@@ -220,7 +234,7 @@ export default {
handlePackageMethodTagClick(item) { handlePackageMethodTagClick(item) {
this.localConfig.packageMethodId = item.id; this.localConfig.packageMethodId = item.id;
}, },
/** /**
* 处理制剂改变(标签点击) * 处理制剂改变(标签点击)
* @param {Object} item - 制剂项 * @param {Object} item - 制剂项
@@ -233,7 +247,7 @@ export default {
this.localConfig.processRuleNoteId = null; this.localConfig.processRuleNoteId = null;
// localConfig 的变化会通过 watch 自动触发 @update:config // localConfig 的变化会通过 watch 自动触发 @update:config
}, },
/** /**
* 处理煎法改变(标签点击) * 处理煎法改变(标签点击)
* @param {Object} item - 煎法项 * @param {Object} item - 煎法项
@@ -245,7 +259,7 @@ export default {
this.localConfig.processRuleNoteId = null; this.localConfig.processRuleNoteId = null;
// localConfig 的变化会通过 watch 自动触发 @update:config // localConfig 的变化会通过 watch 自动触发 @update:config
}, },
/** /**
* 处理备注改变(标签点击) * 处理备注改变(标签点击)
* @param {Object} item - 备注项 * @param {Object} item - 备注项
@@ -254,7 +268,7 @@ export default {
handleProcessRuleNoteTagClick(item) { handleProcessRuleNoteTagClick(item) {
this.localConfig.processRuleNoteId = item.id; this.localConfig.processRuleNoteId = item.id;
}, },
/** /**
* 获取包法名称 * 获取包法名称
* @returns {string} 包法名称 * @returns {string} 包法名称
@@ -263,7 +277,7 @@ export default {
const item = this.packageMethodList.find(item => item.id === this.localConfig.packageMethodId); const item = this.packageMethodList.find(item => item.id === this.localConfig.packageMethodId);
return item?.name || ''; return item?.name || '';
}, },
/** /**
* 获取制剂名称 * 获取制剂名称
* @returns {string} 制剂名称 * @returns {string} 制剂名称
@@ -272,7 +286,7 @@ export default {
const item = this.processRuleList.find(item => item.id === this.localConfig.processRuleId); const item = this.processRuleList.find(item => item.id === this.localConfig.processRuleId);
return item?.name || ''; return item?.name || '';
}, },
/** /**
* 获取煎法名称 * 获取煎法名称
* @returns {string} 煎法名称 * @returns {string} 煎法名称
@@ -281,7 +295,7 @@ export default {
const item = this.childProcessRuleList.find(item => item.id === this.localConfig.childProcessRuleId); const item = this.childProcessRuleList.find(item => item.id === this.localConfig.childProcessRuleId);
return item?.name || ''; return item?.name || '';
}, },
/** /**
* 获取备注名称 * 获取备注名称
* @returns {string} 备注名称 * @returns {string} 备注名称
@@ -295,70 +309,64 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 统一样式 */
.color-title { color: #2A2E35; }
.color-sub { color: #8A92A3; }
.fs-28 { font-size: 28rpx; }
.fs-30 { font-size: 30rpx; }
.font-bold { font-weight: bold; }
.m-b-16 { margin-bottom: 16rpx; }
.m-b-24 { margin-bottom: 24rpx; }
.m-b-32 { margin-bottom: 32rpx; }
.m-t-24 { margin-top: 24rpx; }
.m-l-16 { margin-left: 16rpx; }
.pt-20 { padding-top: 20rpx; }
.pt-32 { padding-top: 32rpx; }
.p-y-24 { padding: 24rpx 0; }
.border-t-dashed { border-top: 1px dashed #E2E8F0; }
.flex-row { display: flex; flex-direction: row; }
.flex-jus-sp { justify-content: space-between; }
.flex-ali-center { align-items: center; }
.chinese-medicine-config { .chinese-medicine-config {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.config-section { .modern-pill-group {
margin-bottom: 32rpx;
}
.section-title {
margin-bottom: 16rpx;
}
.section-content {
display: flex;
align-items: center;
gap: 16rpx;
}
.tag-group {
display: flex; display: flex;
gap: 16rpx; gap: 16rpx;
flex-wrap: wrap; flex-wrap: wrap;
} }
.tag-item { .modern-pill {
padding: 12rpx 32rpx; padding: 12rpx 36rpx;
background-color: #f5f5f5; background-color: #F4F6F8;
color: #666; color: #6C7380;
border-radius: 32rpx; border-radius: 40rpx;
font-size: 28rpx; font-size: 26rpx;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
border: 2rpx solid transparent; border: 1px solid transparent;
transition: all 0.3s; transition: all 0.2s;
}
.tag-item.active { &.active {
background-color: #6ACDBB; background-color: #00A88A;
color: #fff; color: #fff;
border-color: #6ACDBB; border-color: #00A88A;
} font-weight: bold;
}
.picker-view { &.outline {
padding: 16rpx; background-color: #fff;
background: #f9f9f9; border: 1px solid #E2E8F0;
border-radius: 8rpx;
min-width: 200rpx;
text-align: center;
}
.input { &.active {
width: 200rpx; background-color: #E8F6F4;
color: #00A88A;
border-color: #00A88A;
}
}
} }
</style>
.m-t-16 {
margin-top: 16rpx;
}
.m-l-16 {
margin-left: 16rpx;
}
.color9 {
color: #999;
}
</style>

View File

@@ -1,138 +1,144 @@
<template> <template>
<u-popup <u-popup
v-model="show" v-model="show"
mode="bottom" mode="bottom"
:closeable="true" :closeable="true"
@close="handleClose" @close="handleClose"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="false" :mask-close-able="false"
z-index="10078" z-index="10078"
height="80%" height="80%"
> >
<view class="chinese-medicine-modal"> <view class="chinese-medicine-modal page-bg">
<view class="modal-header"> <view class="modal-header white">
<d-text text="选择中药" className="fs-32 main-c"></d-text> <d-text text="选择中药" className="fs-32 font-bold color-title"></d-text>
</view> </view>
<view class="modal-content"> <view class="modal-content">
<!-- 搜索框 --> <!-- 搜索框 -->
<view class="search-box"> <view class="search-box m-b-24">
<u-search <u-search
v-model="searchKey" v-model="searchKey"
placeholder="请输入药品名称搜索" placeholder="请输入药品名称搜索"
@input="handleSearch" @input="handleSearch"
@custom="searchKey = ''; handleSearch()" @custom="searchKey = ''; handleSearch()"
:show-action="false" :show-action="false"
bg-color="#fff"
/> />
</view> </view>
<!-- 药品列表可滚动支持触底加载 --> <!-- 药品列表可滚动支持触底加载 -->
<scroll-view <scroll-view
class="drug-list-scroll" class="drug-list-scroll"
scroll-y scroll-y
@scrolltolower="loadMore" @scrolltolower="loadMore"
:lower-threshold="100" :lower-threshold="100"
> >
<view class="drug-list" v-if="drugList.length > 0"> <view class="drug-list" v-if="drugList.length > 0">
<view <view
class="drug-item white b-r-8 p-32 m-t-2" class="medicine-card"
v-for="(item, index) in drugList" v-for="(item, index) in drugList"
:key="item.id || index" :key="item.id || index"
:class="{ selected: isInSelected(item) }" :class="{ 'is-selected': isInSelected(item) }"
> >
<view class="drug-header flex-row flex-jus-sp flex-ali-center"> <view class="drug-header flex-row flex-jus-sp flex-ali-center m-b-16">
<d-text :text="getDrugName(item)" className="fs-3 color0 bold"></d-text> <view class="flex-row flex-ali-center">
<d-text :text="`¥${parseFloat(item.price || 0).toFixed(2)}/${getUnitName(item)}`" className="fs-3 error-c"></d-text> <view class="selected-indicator" v-if="isInSelected(item)"></view>
<d-text :text="getDrugName(item)" className="fs-32 font-bold color-title"></d-text>
</view>
<d-text :text="`¥${parseFloat(item.price || 0).toFixed(2)}/${getUnitName(item)}`" className="fs-32 font-bold color-price"></d-text>
</view> </view>
<view class="drug-info m-t-16"> <view class="drug-info">
<view class="specification-row flex-row flex-jus-sp flex-ali-center m-b-16"> <view class="specification-row flex-row flex-jus-sp flex-ali-center m-b-24">
<d-text :text="`规格:${getSpecification(item)}`" className="fs-24 color9"></d-text> <d-text :text="`规格:${getSpecification(item)}`" className="fs-26 color-sub"></d-text>
<view class="selected-tag" v-if="isInSelected(item)"></view> <view class="usage-capsule" v-if="isInSelected(item)">加处方</view>
</view> </view>
<view class="common-quantity-tags m-b-16" v-if="commonQuantities.length > 0">
<view <!-- 常用用量快捷标签 -->
class="quantity-tag" <view class="common-quantity-tags m-b-24" v-if="commonQuantities.length > 0">
v-for="qty in commonQuantities" <view
:key="qty" class="modern-tag"
@click.stop="handleSelectCommonQuantity" v-for="qty in commonQuantities"
:data-index="index" :key="qty"
:data-quantity="qty" @click.stop="handleSelectCommonQuantity"
:data-index="index"
:data-quantity="qty"
> >
{{ qty }}g 快捷 {{ qty }}g
</view> </view>
</view> </view>
<view class="quantity-input-row flex-row flex-ali-center flex-jus-sp">
<view class="quantity-input-row flex-row flex-ali-center flex-jus-sp pt-20 border-t-dashed">
<view class="quantity-input-left flex-row flex-ali-center"> <view class="quantity-input-left flex-row flex-ali-center">
<view class="quantity-label">用量</view> <d-text text="用量:" className="fs-28 color-title"></d-text>
<view class="quantity-input-wrapper flex-row flex-ali-center">
<view <!-- 现代精致步进器 -->
class="quantity-btn" <view class="modern-stepper flex-row flex-ali-center">
@click.stop="handleDecreaseQuantity" <view class="stepper-btn" @click.stop="handleDecreaseQuantity" :data-index="index">-</view>
:data-index="index" <input
>-</view> class="stepper-input fs-28 font-bold"
<input type="number"
class="quantity-input" :value="getQuantity(item)"
type="number" @input="handleQuantityInput"
:value="getQuantity(item)" :data-index="index"
@input="handleQuantityInput" placeholder="0"
:data-index="index"
placeholder="请输入"
/> />
<view <view class="stepper-btn" @click.stop="handleIncreaseQuantity" :data-index="index">+</view>
class="quantity-btn"
@click.stop="handleIncreaseQuantity"
:data-index="index"
>+</view>
</view> </view>
<text class="unit-text">{{ getUnitName(item) }}</text> <text class="fs-26 color-sub m-l-12">{{ getUnitName(item) }}</text>
</view> </view>
<view
v-if="isInSelected(item)" <view
class="action-icon delete-icon" v-if="isInSelected(item)"
@click.stop="handleRemoveDrug" class="action-btn-text danger"
:data-index="index" @click.stop="handleRemoveDrug"
:data-index="index"
> >
<u-icon name="trash" size="44" color="#FC3636"></u-icon> <u-icon name="trash" size="32" class="m-r-8"></u-icon> 移除
</view> </view>
<view <view
v-else v-else
class="action-icon add-icon" class="action-btn-ghost primary"
@click.stop="handleAddDrug" @click.stop="handleAddDrug"
:data-index="index" :data-index="index"
> >
<u-icon name="plus-circle" size="44" color="#6ACDBB"></u-icon> <u-icon name="plus" size="24" class="m-r-8"></u-icon> 添加
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 加载更多 --> <!-- 加载更多 -->
<view class="load-more" v-if="loading"> <view class="load-more" v-if="loading">
<text class="load-more-text">加载中...</text> <u-loading mode="circle" color="#6ACDBB"></u-loading> <text class="load-more-text m-l-16">加载中...</text>
</view> </view>
<view class="load-more" v-else-if="!hasMore && drugList.length > 0"> <view class="load-more" v-else-if="!hasMore && drugList.length > 0">
<text class="load-more-text">没有更多了</text> <text class="load-more-text">没有更多了</text>
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view class="empty-state" v-else> <view class="empty-state" v-else>
<d-empty text="暂无药品"></d-empty> <d-empty text="暂无药品"></d-empty>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<view class="modal-footer flex-jus-center flex-ali-center"> <view class="modal-footer flex-jus-center flex-ali-center white shadow-up">
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleClose" @click="handleClose"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#6ACDBB', backgroundColor: '#00A88A',
color: '#fff', color: '#fff',
height: '86rpx', height: '88rpx',
width: '670rpx' width: '670rpx',
fontWeight: 'bold',
fontSize: '32rpx',
boxShadow: '0 8rpx 20rpx rgba(0, 168, 138, 0.25)',
border: 'none'
}" }"
>关闭</u-button> >关闭</u-button>
</view> </view>
@@ -141,6 +147,7 @@
</template> </template>
<script> <script>
// 保留所有原有逻辑代码与注释
import { getProductListDoctorReception } from '@/api/reception.js'; import { getProductListDoctorReception } from '@/api/reception.js';
export default { export default {
@@ -223,7 +230,7 @@ export default {
page: this.page, page: this.page,
page_size: this.pageSize page_size: this.pageSize
}); });
if (res && (res.code === 0 || res.errcode === 0)) { if (res && (res.code === 0 || res.errcode === 0)) {
const list = res.data || res.result || []; const list = res.data || res.result || [];
this.hasMore = list.length >= this.pageSize; this.hasMore = list.length >= this.pageSize;
@@ -269,7 +276,7 @@ export default {
this.loading = false; this.loading = false;
} }
}, },
/** /**
* 搜索药品 * 搜索药品
* 职责:防抖搜索,调用加载药品列表 * 职责:防抖搜索,调用加载药品列表
@@ -289,7 +296,7 @@ export default {
this.loadDrugList(this.searchKey); this.loadDrugList(this.searchKey);
}, 300); }, 300);
}, },
/** /**
* 获取药品数量 * 获取药品数量
* @param {Object} drug - 药品数据 * @param {Object} drug - 药品数据
@@ -298,7 +305,7 @@ export default {
getQuantity(drug) { getQuantity(drug) {
return drug._quantity || 0; return drug._quantity || 0;
}, },
/** /**
* 获取药品名称 * 获取药品名称
* @param {Object} drug - 药品数据 * @param {Object} drug - 药品数据
@@ -316,7 +323,7 @@ export default {
} }
return ''; return '';
}, },
/** /**
* 获取单位名称 * 获取单位名称
* @param {Object} drug - 药品数据 * @param {Object} drug - 药品数据
@@ -331,7 +338,7 @@ export default {
} }
return 'g'; return 'g';
}, },
/** /**
* 获取规格 * 获取规格
* @param {Object} drug - 药品数据 * @param {Object} drug - 药品数据
@@ -346,7 +353,7 @@ export default {
} }
return '--'; return '--';
}, },
/** /**
* 判断是否已选中该中药(基于父组件传入的 currentDrugs初始展示用 * 判断是否已选中该中药(基于父组件传入的 currentDrugs初始展示用
* @param {Object} drug * @param {Object} drug
@@ -492,7 +499,7 @@ export default {
} }
this.emitSelect(); this.emitSelect();
}, },
/** /**
* 增加数量 * 增加数量
* @param {Event} event - 点击事件对象 * @param {Event} event - 点击事件对象
@@ -501,20 +508,20 @@ export default {
handleIncreaseQuantity(event) { handleIncreaseQuantity(event) {
const index = event.currentTarget.dataset.index; const index = event.currentTarget.dataset.index;
console.log('handleIncreaseQuantity called', { index, drugListLength: this.drugList.length }); console.log('handleIncreaseQuantity called', { index, drugListLength: this.drugList.length });
if (index === undefined || index === null) { if (index === undefined || index === null) {
console.error('handleIncreaseQuantity: index is undefined'); console.error('handleIncreaseQuantity: index is undefined');
return; return;
} }
const drug = this.drugList[index]; const drug = this.drugList[index];
console.log('drug found', drug); console.log('drug found', drug);
if (!drug) { if (!drug) {
console.error('handleIncreaseQuantity: drug not found at index', index); console.error('handleIncreaseQuantity: drug not found at index', index);
return; return;
} }
// 确保 _quantity 属性存在 // 确保 _quantity 属性存在
if (typeof drug._quantity === 'undefined') { if (typeof drug._quantity === 'undefined') {
this.$set(drug, '_quantity', 0); this.$set(drug, '_quantity', 0);
@@ -522,7 +529,7 @@ export default {
this.$set(drug, '_quantity', (drug._quantity || 0) + 1); this.$set(drug, '_quantity', (drug._quantity || 0) + 1);
this.syncQuantityToSelected(drug); this.syncQuantityToSelected(drug);
}, },
/** /**
* 减少数量 * 减少数量
* @param {Event} event - 点击事件对象 * @param {Event} event - 点击事件对象
@@ -531,20 +538,20 @@ export default {
handleDecreaseQuantity(event) { handleDecreaseQuantity(event) {
const index = event.currentTarget.dataset.index; const index = event.currentTarget.dataset.index;
console.log('handleDecreaseQuantity called', { index, drugListLength: this.drugList.length }); console.log('handleDecreaseQuantity called', { index, drugListLength: this.drugList.length });
if (index === undefined || index === null) { if (index === undefined || index === null) {
console.error('handleDecreaseQuantity: index is undefined'); console.error('handleDecreaseQuantity: index is undefined');
return; return;
} }
const drug = this.drugList[index]; const drug = this.drugList[index];
console.log('drug found', drug); console.log('drug found', drug);
if (!drug) { if (!drug) {
console.error('handleDecreaseQuantity: drug not found at index', index); console.error('handleDecreaseQuantity: drug not found at index', index);
return; return;
} }
// 确保 _quantity 属性存在 // 确保 _quantity 属性存在
if (typeof drug._quantity === 'undefined') { if (typeof drug._quantity === 'undefined') {
this.$set(drug, '_quantity', 0); this.$set(drug, '_quantity', 0);
@@ -555,7 +562,7 @@ export default {
this.syncQuantityToSelected(drug); this.syncQuantityToSelected(drug);
} }
}, },
/** /**
* 处理数量输入 * 处理数量输入
* @param {Event} event - 输入事件对象 * @param {Event} event - 输入事件对象
@@ -564,33 +571,33 @@ export default {
handleQuantityInput(event) { handleQuantityInput(event) {
const index = event.currentTarget.dataset.index; const index = event.currentTarget.dataset.index;
console.log('handleQuantityInput called', { index, drugListLength: this.drugList.length }); console.log('handleQuantityInput called', { index, drugListLength: this.drugList.length });
if (index === undefined || index === null) { if (index === undefined || index === null) {
console.error('handleQuantityInput: index is undefined'); console.error('handleQuantityInput: index is undefined');
return; return;
} }
const drug = this.drugList[index]; const drug = this.drugList[index];
console.log('drug found', drug); console.log('drug found', drug);
if (!drug) { if (!drug) {
console.error('handleQuantityInput: drug not found at index', index); console.error('handleQuantityInput: drug not found at index', index);
return; return;
} }
if (!event || !event.detail) { if (!event || !event.detail) {
console.error('handleQuantityInput: event or event.detail is undefined'); console.error('handleQuantityInput: event or event.detail is undefined');
return; return;
} }
const value = event.detail.value; const value = event.detail.value;
const numValue = parseFloat(value); const numValue = parseFloat(value);
// 确保 _quantity 属性存在 // 确保 _quantity 属性存在
if (typeof drug._quantity === 'undefined') { if (typeof drug._quantity === 'undefined') {
this.$set(drug, '_quantity', 0); this.$set(drug, '_quantity', 0);
} }
if (!isNaN(numValue) && numValue >= 0) { if (!isNaN(numValue) && numValue >= 0) {
this.$set(drug, '_quantity', numValue); this.$set(drug, '_quantity', numValue);
this.syncQuantityToSelected(drug); this.syncQuantityToSelected(drug);
@@ -599,7 +606,7 @@ export default {
this.syncQuantityToSelected(drug); this.syncQuantityToSelected(drug);
} }
}, },
/** /**
* 选择常用克数 * 选择常用克数
* @param {Event} event - 点击事件对象 * @param {Event} event - 点击事件对象
@@ -609,25 +616,25 @@ export default {
const index = event.currentTarget.dataset.index; const index = event.currentTarget.dataset.index;
const quantity = parseFloat(event.currentTarget.dataset.quantity); const quantity = parseFloat(event.currentTarget.dataset.quantity);
console.log('handleSelectCommonQuantity called', { index, quantity, drugListLength: this.drugList.length }); console.log('handleSelectCommonQuantity called', { index, quantity, drugListLength: this.drugList.length });
if (index === undefined || index === null) { if (index === undefined || index === null) {
console.error('handleSelectCommonQuantity: index is undefined'); console.error('handleSelectCommonQuantity: index is undefined');
return; return;
} }
if (isNaN(quantity)) { if (isNaN(quantity)) {
console.error('handleSelectCommonQuantity: quantity is NaN', event.currentTarget.dataset.quantity); console.error('handleSelectCommonQuantity: quantity is NaN', event.currentTarget.dataset.quantity);
return; return;
} }
const drug = this.drugList[index]; const drug = this.drugList[index];
console.log('drug found', drug); console.log('drug found', drug);
if (!drug) { if (!drug) {
console.error('handleSelectCommonQuantity: drug not found at index', index); console.error('handleSelectCommonQuantity: drug not found at index', index);
return; return;
} }
// 确保 _quantity 属性存在 // 确保 _quantity 属性存在
if (typeof drug._quantity === 'undefined') { if (typeof drug._quantity === 'undefined') {
this.$set(drug, '_quantity', 0); this.$set(drug, '_quantity', 0);
@@ -636,7 +643,7 @@ export default {
this.$set(drug, '_quantity', quantity); this.$set(drug, '_quantity', quantity);
this.syncQuantityToSelected(drug); this.syncQuantityToSelected(drug);
}, },
/** /**
* 触底加载更多 * 触底加载更多
*/ */
@@ -648,7 +655,7 @@ export default {
this.page += 1; this.page += 1;
this.loadDrugList(this.searchKey); this.loadDrugList(this.searchKey);
}, },
/** /**
* 关闭弹窗 * 关闭弹窗
* 职责关闭弹窗触发input事件 * 职责关闭弹窗触发input事件
@@ -663,18 +670,41 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 使用与 index.vue 统一的设计风格与变量 */
.page-bg { background-color: #F4F6F8; }
.white { background: #fff; }
.color-title { color: #2A2E35; }
.color-sub { color: #8A92A3; }
.color-price { color: #F53F3F; }
.fs-26 { font-size: 26rpx; }
.fs-28 { font-size: 28rpx; }
.fs-32 { font-size: 32rpx; }
.font-bold { font-weight: bold; }
.m-b-16 { margin-bottom: 16rpx; }
.m-b-24 { margin-bottom: 24rpx; }
.m-r-8 { margin-right: 8rpx; }
.m-l-12 { margin-left: 12rpx; }
.m-l-16 { margin-left: 16rpx; }
.pt-20 { padding-top: 20rpx; }
.border-t-dashed { border-top: 1px dashed #E2E8F0; }
.shadow-up { box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
.flex-row { display: flex; flex-direction: row; }
.flex-jus-sp { justify-content: space-between; }
.flex-jus-center { justify-content: center; }
.flex-ali-center { align-items: center; }
.chinese-medicine-modal { .chinese-medicine-modal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #fff; overflow: hidden;
overflow: hidden; // 防止整体溢出
} }
.modal-header { .modal-header {
padding: 32rpx; padding: 32rpx;
text-align: center; text-align: center;
border-bottom: 1rpx solid #eee; border-bottom: 1px solid #F0F2F5;
} }
.modal-content { .modal-content {
@@ -682,209 +712,158 @@ export default {
padding: 32rpx; padding: 32rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; // 防止内容溢出 overflow: hidden;
} }
.drug-list-scroll { .drug-list-scroll {
flex: 1; flex: 1;
overflow-y: auto; // 确保可以滚动 overflow-y: auto;
min-height: 0; // 重要:允许 flex 子元素缩小 min-height: 0;
} display: flex;
flex-direction: column;
.search-box {
margin-bottom: 32rpx;
} }
/* 底部防遮挡边距 */
.drug-list { .drug-list {
padding-bottom: 32rpx; padding-bottom: 24rpx;
} }
.drug-item { /* 底部防遮挡边距 */
margin-bottom: 16rpx; .drug-list {
padding-bottom: 120rpx;
} }
.drug-item.selected { /* 现代卡片样式 */
border: 2rpx solid #6ACDBB; .medicine-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(42, 46, 53, 0.04);
position: relative;
overflow: hidden;
border: 2rpx solid transparent;
transition: all 0.2s;
&.is-selected {
border-color: #00A88A;
background-color: #F8FDFB;
}
} }
.drug-header { .selected-indicator {
margin-bottom: 16rpx; width: 8rpx;
height: 32rpx;
background-color: #00A88A;
border-radius: 4rpx;
margin-right: 12rpx;
} }
.drug-info { .usage-capsule {
margin-bottom: 16rpx; display: inline-flex;
} align-items: center;
background: #E8F6F4;
.specification-row { color: #00A88A;
margin-bottom: 16rpx; padding: 6rpx 16rpx;
} border-radius: 6rpx;
font-size: 24rpx;
.selected-tag { font-weight: bold;
padding: 4rpx 16rpx;
border-radius: 32rpx;
border: 1rpx solid #6ACDBB;
color: #6ACDBB;
font-size: 22rpx;
}
.m-b-16 {
margin-bottom: 16rpx;
} }
.common-quantity-tags { .common-quantity-tags {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 16rpx; gap: 16rpx;
margin-bottom: 16rpx;
} }
.quantity-tag { .modern-tag {
padding: 8rpx 24rpx; padding: 10rpx 24rpx;
background-color: #f0f5ff; background-color: #F4F6F8;
color: #6ACDBB;
border: 1rpx solid #6ACDBB;
border-radius: 32rpx;
font-size: 24rpx;
text-align: center;
cursor: pointer;
}
.quantity-tag:active {
background-color: #6ACDBB;
color: #fff;
}
.quantity-input-row {
gap: 16rpx;
align-items: center;
}
.quantity-input-left {
display: flex;
flex-direction: row;
align-items: center;
gap: 16rpx;
flex: 1;
min-width: 0;
}
.action-icon {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.action-icon:active {
opacity: 0.7;
}
.quantity-label {
font-size: 28rpx;
color: #6C7380; color: #6C7380;
} border-radius: 30rpx;
.quantity-input-wrapper {
gap: 16rpx;
align-items: center;
}
.quantity-btn {
width: 48rpx;
height: 48rpx;
line-height: 48rpx;
text-align: center;
border: 1rpx solid #6ACDBB;
color: #6ACDBB;
border-radius: 8rpx;
font-size: 32rpx;
flex-shrink: 0;
}
.quantity-input {
width: 120rpx;
height: 48rpx;
line-height: 48rpx;
text-align: center;
border: 1rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 28rpx;
padding: 0 16rpx;
background-color: #fff;
}
.unit-text {
font-size: 24rpx; font-size: 24rpx;
color: #999; cursor: pointer;
margin-left: 8rpx; transition: all 0.2s;
&:active {
background-color: #00A88A;
color: #fff;
}
}
/* 现代精致步进器 */
.modern-stepper {
background: #F7F8FA;
border-radius: 8rpx;
overflow: hidden;
border: 1px solid #E2E8F0;
.stepper-btn {
width: 60rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
color: #6C7380;
font-size: 36rpx;
font-weight: bold;
background: #fff;
cursor: pointer;
}
.stepper-input {
width: 80rpx;
height: 56rpx;
text-align: center;
color: #2A2E35;
background: #F7F8FA;
border-left: 1px solid #E2E8F0;
border-right: 1px solid #E2E8F0;
}
}
.action-btn-ghost {
padding: 10rpx 28rpx;
border-radius: 30rpx;
font-size: 26rpx;
font-weight: bold;
display: flex;
align-items: center;
&.primary {
color: #00A88A;
background: #E8F6F4;
}
}
.action-btn-text {
display: flex;
align-items: center;
font-size: 26rpx;
font-weight: bold;
&.danger { color: #8A92A3; }
}
.load-more {
display: flex;
justify-content: center;
align-items: center;
padding: 32rpx 0;
.load-more-text {
font-size: 24rpx;
color: #999;
}
} }
.empty-state { .empty-state {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 100rpx 0; padding: 100rpx 0;
text-align: center; text-align: center;
} }
.modal-footer { .modal-footer {
display: flex; padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
flex-direction: row;
align-items: center;
justify-content: center;
padding: 32rpx;
border-top: 1rpx solid #eee;
background-color: #fff; background-color: #fff;
position: sticky;
bottom: 0;
z-index: 10;
} }
</style>
.white {
background: #fff;
}
.b-r-8 {
border-radius: 8rpx;
}
.p-32 {
padding: 32rpx;
}
.m-t-2 {
margin-top: 16rpx;
}
.m-t-16 {
margin-top: 16rpx;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-jus-sp {
justify-content: space-between;
}
.flex-jus-center {
justify-content: center;
}
.flex-ali-center {
align-items: center;
}
.color0 {
color: #000;
}
.color9 {
color: #999;
}
.bold {
font-weight: bold;
}
</style>

View File

@@ -1,11 +1,11 @@
<template> <template>
<u-popup <u-popup
v-model="show" v-model="show"
mode="bottom" mode="bottom"
:closeable="true" :closeable="true"
@close="handleClose" @close="handleClose"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="false" :mask-close-able="false"
z-index="10078" z-index="10078"
height="80%" height="80%"
> >
@@ -13,13 +13,13 @@
<view class="modal-header"> <view class="modal-header">
<d-text text="常用方" className="fs-32 main-c"></d-text> <d-text text="常用方" className="fs-32 main-c"></d-text>
</view> </view>
<view class="modal-content"> <view class="modal-content">
<!-- 常用方列表 --> <!-- 常用方列表 -->
<view class="prescription-list" v-if="prescriptionList.length > 0"> <view class="prescription-list" v-if="prescriptionList.length > 0">
<view <view
class="prescription-item white b-r-8 m-t-2" class="prescription-item white b-r-8 m-t-2"
v-for="(item, index) in prescriptionList" v-for="(item, index) in prescriptionList"
:key="item.id" :key="item.id"
@click="handleSelectPrescription(item, index)" @click="handleSelectPrescription(item, index)"
> >
@@ -35,8 +35,8 @@
<view class="item-content m-t-16"> <view class="item-content m-t-16">
<view class="label">药品</view> <view class="label">药品</view>
<view class="drug-list"> <view class="drug-list">
<text <text
v-for="(drug, idx) in getDrugList(item, index)" v-for="(drug, idx) in getDrugList(item, index)"
:key="idx" :key="idx"
class="drug-item" class="drug-item"
> >
@@ -50,13 +50,13 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view class="empty-state" v-else> <view class="empty-state" v-else>
<d-empty text="暂无常用方"></d-empty> <d-empty text="暂无常用方"></d-empty>
</view> </view>
</view> </view>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<view class="modal-footer flex-jus-between flex-ali-center" v-if="!isSelectMode"> <view class="modal-footer flex-jus-between flex-ali-center" v-if="!isSelectMode">
<u-button <u-button
@@ -87,7 +87,7 @@
</template> </template>
<script> <script>
import { getCommonPrescriptionListApi } from '@/api/reception.js'; import { getCommonPrescriptionListApi, deleteCommonPrescriptionApi } from '@/api/reception.js';
export default { export default {
name: 'CommonPrescriptionModal', name: 'CommonPrescriptionModal',
@@ -137,7 +137,7 @@ export default {
return 'tag-granular'; return 'tag-granular';
} }
}, },
/** /**
* 获取标签文本(计算属性) * 获取标签文本(计算属性)
* @returns {string} 标签文本 * @returns {string} 标签文本
@@ -176,7 +176,7 @@ export default {
const storeId = uni.getStorageSync('store_id') || 11001; const storeId = uni.getStorageSync('store_id') || 11001;
const type = this.getCommonPrescriptionType(this.prescriptionType); const type = this.getCommonPrescriptionType(this.prescriptionType);
const res = await getCommonPrescriptionListApi(storeId); const res = await getCommonPrescriptionListApi(storeId);
if (res && res.code === 0) { if (res && res.code === 0) {
// 根据类型过滤常用方主表行 + 平行药品行 // 根据类型过滤常用方主表行 + 平行药品行
const listKey = this.getListKey(type); const listKey = this.getListKey(type);
@@ -196,7 +196,7 @@ export default {
this.loading = false; this.loading = false;
} }
}, },
/** /**
* 获取常用方类型字符串 * 获取常用方类型字符串
* @param {number} type - 处方类型 * @param {number} type - 处方类型
@@ -211,7 +211,7 @@ export default {
return 'granular'; return 'granular';
} }
}, },
/** /**
* 获取列表键名 * 获取列表键名
* @param {string} type - 常用方类型 * @param {string} type - 常用方类型
@@ -226,7 +226,7 @@ export default {
return 'granular_prescription'; return 'granular_prescription';
} }
}, },
/** /**
* 获取某条常用方对应的药品列表 * 获取某条常用方对应的药品列表
* 与 PC 一致list 接口返回的主表行与药品行按下标对齐,从 recipesByIndex[index] 取 * 与 PC 一致list 接口返回的主表行与药品行按下标对齐,从 recipesByIndex[index] 取
@@ -237,7 +237,7 @@ export default {
getDrugList(_prescription, index) { getDrugList(_prescription, index) {
return this.recipesByIndex[index] || []; return this.recipesByIndex[index] || [];
}, },
/** /**
* 格式化药品名称 * 格式化药品名称
* @param {Object} drug - 药品数据 * @param {Object} drug - 药品数据
@@ -251,7 +251,7 @@ export default {
return drug; return drug;
} }
} }
if (this.prescriptionType === 1) { if (this.prescriptionType === 1) {
// 中药 // 中药
const name = drug.drug_name || drug.name || ''; const name = drug.drug_name || drug.name || '';
@@ -265,7 +265,7 @@ export default {
return `${name} *${number}`; return `${name} *${number}`;
} }
}, },
/** /**
* 获取列表项的唯一key * 获取列表项的唯一key
* @param {Object} item - 列表项 * @param {Object} item - 列表项
@@ -275,7 +275,7 @@ export default {
getItemKey(item, index) { getItemKey(item, index) {
return item.id || `item_${index}`; return item.id || `item_${index}`;
}, },
/** /**
* 选择常用方 * 选择常用方
* 直接 emit 整条主表行(已包含后端组装好的 apply_payload * 直接 emit 整条主表行(已包含后端组装好的 apply_payload
@@ -287,7 +287,7 @@ export default {
this.$emit('select', item); this.$emit('select', item);
this.handleClose(); this.handleClose();
}, },
/** /**
* 删除常用方 * 删除常用方
* @param {Object} prescription - 常用方数据 * @param {Object} prescription - 常用方数据
@@ -302,10 +302,18 @@ export default {
success: async ({ confirm }) => { success: async ({ confirm }) => {
if (confirm) { if (confirm) {
try { try {
// TODO: 调用删除接口 const type = this.getCommonPrescriptionType(this.prescriptionType);
// await deleteCommonPrescriptionApi(prescription.id); const res = await deleteCommonPrescriptionApi({
this.$toast('删除成功'); id: prescription.id,
this.prescriptionList.splice(index, 1); type
});
if (res && res.code === 0) {
this.prescriptionList.splice(index, 1);
this.recipesByIndex.splice(index, 1);
this.$toast('删除成功');
} else {
this.$toast(res.msg || res.message || '删除失败');
}
} catch (error) { } catch (error) {
console.error('删除常用方失败:', error); console.error('删除常用方失败:', error);
this.$toast('删除失败'); this.$toast('删除失败');
@@ -314,17 +322,21 @@ export default {
} }
}); });
}, },
/** /**
* 新建常用方 * 新建常用方
* 职责:关闭弹窗,触发新建事件 * 职责:关闭弹窗,触发新建事件
*/ */
handleAddNew() { handleAddNew() {
const initialCategory =
this.prescriptionType === 1 ? 1 : this.prescriptionType === 2 ? 2 : 3;
uni.setStorageSync('add', true);
this.handleClose(); this.handleClose();
// 可以触发新建事件,让父组件处理 uni.navigateTo({
// this.$emit('add-new'); url: `/subPackages/sub_workbench/prescription_v2/index?save_common=1&initial_category=${initialCategory}`
});
}, },
/** /**
* 关闭弹窗 * 关闭弹窗
* 职责关闭弹窗触发input事件 * 职责关闭弹窗触发input事件
@@ -381,15 +393,15 @@ export default {
color: #fff; color: #fff;
font-size: 24rpx; font-size: 24rpx;
text-align: center; text-align: center;
&.tag-chinese { &.tag-chinese {
background: rgba(15, 190, 22, 0.55); background: rgba(15, 190, 22, 0.55);
} }
&.tag-west { &.tag-west {
background: rgba(15, 163, 190, 0.55); background: rgba(15, 163, 190, 0.55);
} }
&.tag-granular { &.tag-granular {
background: #6ACDBB; background: #6ACDBB;
} }
@@ -431,19 +443,19 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
border: 1rpx solid rgba(242, 242, 242, 1); border: 1rpx solid rgba(242, 242, 242, 1);
.btn { .btn {
flex: 1; flex: 1;
height: 86rpx; height: 86rpx;
line-height: 86rpx; line-height: 86rpx;
text-align: center; text-align: center;
font-size: 28rpx; font-size: 28rpx;
&.left { &.left {
border-right: 1rpx solid rgba(242, 242, 242, 1); border-right: 1rpx solid rgba(242, 242, 242, 1);
color: #FC3636; color: #FC3636;
} }
&.right { &.right {
color: #6ACDBB; color: #6ACDBB;
} }

View File

@@ -1,125 +1,134 @@
<template> <template>
<u-popup <u-popup
v-model="show" v-model="show"
mode="bottom" mode="bottom"
:closeable="true" :closeable="true"
@close="handleClose" @close="handleClose"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="false" :mask-close-able="false"
z-index="10078" z-index="10078"
height="80%" height="80%"
> >
<view class="diagnosis-modal"> <view class="diagnosis-modal page-bg">
<view class="modal-header"> <view class="modal-header white">
<d-text text="常用诊断" className="fs-32 main-c"></d-text> <d-text text="常用诊断" className="fs-32 font-bold color-title"></d-text>
</view> </view>
<view class="modal-content"> <view class="modal-content">
<!-- 搜索框 --> <!-- 搜索框 -->
<view class="search-box"> <view class="search-box">
<u-input <u-input
v-model="searchKey" v-model="searchKey"
placeholder="请输入想要搜索的诊断名称..." placeholder="请输入想要搜索的诊断名称..."
:custom-style="searchStyle" :custom-style="searchStyle"
@input="handleSearch" @input="handleSearch"
@clear="handleSearch" @clear="handleSearch"
> >
<template slot="suffix"> <template slot="suffix">
<u-icon name="search" size="20" color="#999"></u-icon> <u-icon name="search" size="32" color="#B0B6C2"></u-icon>
</template> </template>
</u-input> </u-input>
</view> </view>
<!-- 常用诊断列表 --> <view class="scroll-container">
<view class="section" v-if="doctorMyDiseaseList.length > 0"> <!-- 常用诊断列表 -->
<view class="section-header"> <view class="section white radius-16 p-32 m-b-24 shadow-sm" v-if="doctorMyDiseaseList.length > 0">
<d-text text="常用诊断" className="fs-28 content-c"></d-text> <view class="section-header flex-row flex-jus-sp flex-ali-center m-b-24">
<d-text :text="`${doctorMyDiseaseList.length}`" className="fs-24 tips-c"></d-text> <d-text text="常用诊断" className="fs-30 font-bold color-title"></d-text>
</view> <d-text :text="`${doctorMyDiseaseList.length}项`" className="fs-26 color-sub"></d-text>
<view class="tag-container">
<view
v-for="item in doctorMyDiseaseList"
:key="item.id"
class="tag-item"
:class="{ 'selected': item.disease.isSelect === 1 }"
@click="selectDiagnosis(item.disease)"
>
<text class="tag-text">{{ item.disease.name }}</text>
<view class="tag-action" @click.stop="removeFromMyDiagnosis(item)">
<u-icon name="close" size="14" color="#999"></u-icon>
</view>
</view> </view>
</view> <view class="modern-tag-container">
</view> <view
v-for="item in doctorMyDiseaseList"
<!-- 搜索结果列表 --> :key="item.id"
<view class="section" v-if="allDiagnosisList.length > 0"> class="modern-tag-item"
<view class="section-header"> :class="{ 'is-selected': item.disease.isSelect === 1 }"
<d-text text="搜索结果" className="fs-28 content-c"></d-text> @click="selectDiagnosis(item.disease)"
<d-text :text="`共 ${allDiagnosisList.length} 条`" className="fs-24 tips-c"></d-text>
</view>
<scroll-view
class="tag-container-scroll"
scroll-y
@scrolltolower="loadMore"
:lower-threshold="100"
>
<view class="tag-container">
<view
v-for="item in allDiagnosisList"
:key="item.id"
class="tag-item"
:class="{ 'selected': item.isSelect === 1 }"
@click="selectDiagnosis(item)"
> >
<text class="tag-text">{{ item.name }}</text> <text class="tag-text">{{ item.disease.name }}</text>
<view class="tag-action" @click.stop="addToMyDiagnosis(item)" v-if="!item.isInMyList"> <view class="tag-action" @click.stop="removeFromMyDiagnosis(item)">
<u-icon name="plus" size="14" color="#6ACDBB"></u-icon> <u-icon name="close" size="20"></u-icon>
</view>
<view class="tag-action" v-else>
<u-icon name="checkmark" size="14" color="#6ACDBB"></u-icon>
</view> </view>
</view> </view>
</view> </view>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="loading"> <!-- 搜索结果列表 -->
<text class="load-more-text">加载中...</text> <view class="section white radius-16 p-32 shadow-sm" v-if="allDiagnosisList.length > 0">
<view class="section-header flex-row flex-jus-sp flex-ali-center m-b-24">
<d-text text="搜索结果" className="fs-30 font-bold color-title"></d-text>
<d-text :text="`共 ${allDiagnosisList.length} 条`" className="fs-26 color-sub"></d-text>
</view> </view>
<view class="load-more" v-else-if="!hasMore && allDiagnosisList.length > 0"> <scroll-view
<text class="load-more-text">没有更多了</text> class="tag-container-scroll"
</view> scroll-y
</scroll-view> @scrolltolower="loadMore"
</view> :lower-threshold="100"
>
<!-- 空状态 --> <view class="modern-tag-container">
<view class="empty-state" v-if="allDiagnosisList.length === 0 && searchKey"> <view
<d-empty text="暂无相关诊断"></d-empty> v-for="item in allDiagnosisList"
:key="item.id"
class="modern-tag-item"
:class="{ 'is-selected': item.isSelect === 1 }"
@click="selectDiagnosis(item)"
>
<text class="tag-text">{{ item.name }}</text>
<view class="tag-action" @click.stop="addToMyDiagnosis(item)" v-if="!item.isInMyList">
<u-icon name="plus" size="20"></u-icon>
</view>
<view class="tag-action" v-else>
<u-icon name="checkmark" size="20"></u-icon>
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="loading">
<u-loading mode="circle" color="#00A88A"></u-loading> <text class="load-more-text m-l-12">加载中...</text>
</view>
<view class="load-more" v-else-if="!hasMore && allDiagnosisList.length > 0">
<text class="load-more-text">没有更多了</text>
</view>
</scroll-view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="allDiagnosisList.length === 0 && searchKey">
<d-empty text="暂无相关诊断"></d-empty>
</view>
</view> </view>
</view> </view>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<view class="modal-footer flex-jus-between flex-ali-center"> <view class="modal-footer flex-row flex-jus-between flex-ali-center white shadow-up">
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleClose" @click="handleClose"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#f5f5f5', backgroundColor: '#F4F6F8',
color: '#333', color: '#2A2E35',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
border: 'none'
}" }"
>取消</u-button> >取消</u-button>
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleConfirm" @click="handleConfirm"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#6ACDBB', backgroundColor: '#00A88A',
color: '#fff', color: '#fff',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
boxShadow: '0 8rpx 20rpx rgba(0, 168, 138, 0.25)',
border: 'none'
}" }"
>确定</u-button> >确定</u-button>
</view> </view>
@@ -128,6 +137,7 @@
</template> </template>
<script> <script>
// 保留所有原有逻辑代码与注释
import { getDiseaseList, getMyDiseaseList, addMyDisease, deleteMyDisease } from '@/api/reception.js'; import { getDiseaseList, getMyDiseaseList, addMyDisease, deleteMyDisease } from '@/api/reception.js';
export default { export default {
@@ -162,10 +172,11 @@ export default {
searchTimer: null, searchTimer: null,
searchStyle: { searchStyle: {
fontSize: '28rpx', fontSize: '28rpx',
backgroundColor: '#F3F4F5', backgroundColor: '#fff',
borderRadius: '66rpx', borderRadius: '16rpx',
height: '66rpx', height: '80rpx',
padding: '8rpx 32rpx' padding: '0 32rpx',
boxShadow: '0 4rpx 16rpx rgba(0,0,0,0.02)'
} }
}; };
}, },
@@ -201,7 +212,7 @@ export default {
const res = await getDiseaseList(searchKey, page, this.pageSize); const res = await getDiseaseList(searchKey, page, this.pageSize);
let list = []; let list = [];
let hasMore = false; let hasMore = false;
// 正确解析响应数据 // 正确解析响应数据
if (res && (res.code === 0 || res.errcode === 0)) { if (res && (res.code === 0 || res.errcode === 0)) {
// 优先使用 data然后是 result // 优先使用 data然后是 result
@@ -212,7 +223,7 @@ export default {
} else if (Array.isArray(res)) { } else if (Array.isArray(res)) {
list = res; list = res;
} }
// 从响应中获取分页信息 // 从响应中获取分页信息
if (res.pagination) { if (res.pagination) {
hasMore = res.pagination.has_more || false; hasMore = res.pagination.has_more || false;
@@ -223,11 +234,11 @@ export default {
hasMore = list.length >= this.pageSize; hasMore = list.length >= this.pageSize;
} }
} }
const processedList = list.map((item) => { const processedList = list.map((item) => {
item.isSelect = 0; item.isSelect = 0;
item.isInMyList = this.doctorMyDiseaseList.some( item.isInMyList = this.doctorMyDiseaseList.some(
(myItem) => myItem.disease && myItem.disease.id === item.id (myItem) => myItem.disease && myItem.disease.id === item.id
); );
if (this.selectDiagnosisList) { if (this.selectDiagnosisList) {
const valuesArr = this.selectDiagnosisList.split(''); const valuesArr = this.selectDiagnosisList.split('');
@@ -239,14 +250,14 @@ export default {
} }
return item; return item;
}); });
if (append) { if (append) {
this.allDiagnosisList = [...this.allDiagnosisList, ...processedList]; this.allDiagnosisList = [...this.allDiagnosisList, ...processedList];
} else { } else {
this.allDiagnosisList = processedList; this.allDiagnosisList = processedList;
this.currentPage = 1; this.currentPage = 1;
} }
this.hasMore = hasMore; this.hasMore = hasMore;
this.loading = false; this.loading = false;
} catch (error) { } catch (error) {
@@ -256,7 +267,7 @@ export default {
this.hasMore = false; this.hasMore = false;
} }
}, },
/** /**
* 加载常用诊断列表 * 加载常用诊断列表
* 职责:调用接口获取医生的常用诊断列表 * 职责:调用接口获取医生的常用诊断列表
@@ -265,7 +276,7 @@ export default {
try { try {
const res = await getMyDiseaseList(); const res = await getMyDiseaseList();
let list = []; let list = [];
// 正确解析响应数据 // 正确解析响应数据
if (res && (res.code === 0 || res.errcode === 0)) { if (res && (res.code === 0 || res.errcode === 0)) {
if (res.data && Array.isArray(res.data)) { if (res.data && Array.isArray(res.data)) {
@@ -276,12 +287,12 @@ export default {
list = res; list = res;
} }
} }
// 确保 list 是数组,避免 map 报错 // 确保 list 是数组,避免 map 报错
if (!Array.isArray(list)) { if (!Array.isArray(list)) {
list = []; list = [];
} }
this.doctorMyDiseaseList = list.map((item) => { this.doctorMyDiseaseList = list.map((item) => {
// 确保 item.disease 存在 // 确保 item.disease 存在
if (item.disease) { if (item.disease) {
@@ -302,7 +313,7 @@ export default {
this.doctorMyDiseaseList = []; this.doctorMyDiseaseList = [];
} }
}, },
/** /**
* 搜索诊断 * 搜索诊断
* 职责:防抖搜索,只在有输入时调用加载诊断列表 * 职责:防抖搜索,只在有输入时调用加载诊断列表
@@ -323,7 +334,7 @@ export default {
} }
}, 300); }, 300);
}, },
/** /**
* 触底加载更多 * 触底加载更多
* 职责:加载下一页数据 * 职责:加载下一页数据
@@ -336,7 +347,7 @@ export default {
this.currentPage++; this.currentPage++;
this.loadDiagnosisList(this.searchKey.trim(), this.currentPage, true); this.loadDiagnosisList(this.searchKey.trim(), this.currentPage, true);
}, },
/** /**
* 选择诊断 * 选择诊断
* @param {Object} item - 诊断项 * @param {Object} item - 诊断项
@@ -344,7 +355,7 @@ export default {
*/ */
selectDiagnosis(item) { selectDiagnosis(item) {
let arr = this.selectDiagnosisList ? this.selectDiagnosisList.split('').filter(Boolean) : []; let arr = this.selectDiagnosisList ? this.selectDiagnosisList.split('').filter(Boolean) : [];
if (item.isSelect === 1) { if (item.isSelect === 1) {
arr = arr.filter(name => name !== item.name); arr = arr.filter(name => name !== item.name);
item.isSelect = 0; item.isSelect = 0;
@@ -354,10 +365,10 @@ export default {
} }
item.isSelect = 1; item.isSelect = 1;
} }
this.selectDiagnosisList = arr.join(''); this.selectDiagnosisList = arr.join('');
}, },
/** /**
* 添加到常用诊断 * 添加到常用诊断
* @param {Object} item - 诊断项 * @param {Object} item - 诊断项
@@ -378,7 +389,7 @@ export default {
this.$toast('添加失败'); this.$toast('添加失败');
} }
}, },
/** /**
* 从常用诊断中删除 * 从常用诊断中删除
* @param {Object} item - 诊断项 * @param {Object} item - 诊断项
@@ -402,7 +413,7 @@ export default {
this.$toast('删除失败'); this.$toast('删除失败');
} }
}, },
/** /**
* 确认选择 * 确认选择
* 职责:触发确认事件,传递选中的诊断文本 * 职责:触发确认事件,传递选中的诊断文本
@@ -411,7 +422,7 @@ export default {
this.$emit('confirm', this.selectDiagnosisList); this.$emit('confirm', this.selectDiagnosisList);
this.handleClose(); this.handleClose();
}, },
/** /**
* 关闭弹窗 * 关闭弹窗
* 职责关闭弹窗触发input事件 * 职责关闭弹窗触发input事件
@@ -425,104 +436,133 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 使用与 index.vue 统一的设计风格与变量 */
.page-bg { background-color: #F4F6F8; }
.white { background: #fff; }
.color-title { color: #2A2E35; }
.color-sub { color: #8A92A3; }
.fs-26 { font-size: 26rpx; }
.fs-28 { font-size: 28rpx; }
.fs-30 { font-size: 30rpx; }
.fs-32 { font-size: 32rpx; }
.font-bold { font-weight: bold; }
.m-b-24 { margin-bottom: 24rpx; }
.m-l-12 { margin-left: 12rpx; }
.p-32 { padding: 32rpx; }
.radius-16 { border-radius: 16rpx; }
.shadow-sm { box-shadow: 0 4rpx 16rpx rgba(42, 46, 53, 0.04); }
.shadow-up { box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
.flex-row { display: flex; flex-direction: row; }
.flex-jus-sp { justify-content: space-between; }
.flex-jus-between { justify-content: space-between; }
.flex-ali-center { align-items: center; }
.diagnosis-modal { .diagnosis-modal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #fff; overflow: hidden;
} }
.modal-header { .modal-header {
padding: 32rpx; padding: 32rpx;
text-align: center; text-align: center;
border-bottom: 1rpx solid #eee; border-bottom: 1px solid #F0F2F5;
} }
.modal-content { .modal-content {
flex: 1; flex: 1;
overflow-y: auto; display: flex;
flex-direction: column;
overflow: hidden;
padding: 32rpx; padding: 32rpx;
} }
.scroll-container {
flex: 1;
overflow-y: auto;
/* 底部防遮挡边距 */
padding-bottom: 24rpx;
display: flex;
flex-direction: column;
}
.search-box { .search-box {
margin-bottom: 32rpx; margin-bottom: 32rpx;
} }
.section {
margin-bottom: 32rpx;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
}
.tag-container-scroll { .tag-container-scroll {
max-height: 60vh; max-height: 50vh;
} }
.tag-container { .modern-tag-container {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 16rpx; gap: 20rpx;
padding-bottom: 32rpx; }
.modern-tag-item {
display: inline-flex;
align-items: center;
padding: 12rpx 24rpx;
background-color: #F7F8FA;
border: 1px solid #E2E8F0;
border-radius: 36rpx;
cursor: pointer;
transition: all 0.2s;
&.is-selected {
background-color: #E8F6F4;
border-color: #00A88A;
.tag-text {
color: #00A88A;
font-weight: bold;
}
.tag-action {
color: #00A88A;
}
}
.tag-text {
font-size: 26rpx;
color: #4b5563;
}
.tag-action {
margin-left: 12rpx;
display: flex;
align-items: center;
color: #8A92A3;
}
} }
.load-more { .load-more {
text-align: center; display: flex;
align-items: center;
justify-content: center;
padding: 32rpx 0; padding: 32rpx 0;
.load-more-text { .load-more-text {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #999;
} }
} }
.tag-item {
display: inline-flex;
align-items: center;
padding: 12rpx 24rpx;
background-color: #f9fafb;
border: 1rpx solid #e5e7eb;
border-radius: 9999rpx;
cursor: pointer;
transition: all 0.2s;
&.selected {
background-color: #eff6ff;
border-color: #6ACDBB;
.tag-text {
color: #6ACDBB;
font-weight: 500;
}
}
.tag-text {
font-size: 28rpx;
color: #4b5563;
}
.tag-action {
margin-left: 8rpx;
display: flex;
align-items: center;
}
}
.empty-state { .empty-state {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 100rpx 0; padding: 100rpx 0;
text-align: center; text-align: center;
} }
.modal-footer { .modal-footer {
display: flex; display: flex;
flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 32rpx; padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #eee; background-color: #fff;
} }
</style> </style>

View File

@@ -1,119 +1,124 @@
<template> <template>
<u-popup <u-popup
v-model="show" v-model="show"
mode="bottom" mode="bottom"
:closeable="true" :closeable="true"
@close="handleClose" @close="handleClose"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="false" :mask-close-able="false"
z-index="10078" z-index="10078"
height="80%" height="80%"
> >
<view class="doctor-order-modal"> <view class="doctor-order-modal page-bg">
<view class="modal-header"> <view class="modal-header white">
<d-text text="常用医嘱" className="fs-32 main-c"></d-text> <d-text text="常用医嘱" className="fs-32 font-bold color-title"></d-text>
</view> </view>
<view class="modal-content"> <view class="modal-content">
<!-- 我的医嘱 --> <!-- 我的医嘱 -->
<view class="section"> <view class="section white radius-16 p-32 m-b-24 shadow-sm">
<view class="section-header"> <view class="section-header flex-row flex-jus-sp flex-ali-center m-b-24">
<d-text text="我的医嘱" className="fs-28 content-c"></d-text> <d-text text="我的医嘱" className="fs-30 font-bold color-title"></d-text>
<view class="add-btn" @click="showAddInput = true" v-if="!showAddInput"> <view class="add-btn-modern" @click="openAddModal">
<u-icon name="plus" size="16" color="#6ACDBB"></u-icon> <u-icon name="plus" size="24" color="#00A88A" class="m-r-8"></u-icon>
<text class="add-text">添加医嘱</text> <text class="add-text">添加医嘱</text>
</view> </view>
</view> </view>
<!-- 添加输入框 -->
<view class="add-input-box" v-if="showAddInput">
<u-input
v-model="newOrderContent"
placeholder="输入内容后确认"
:custom-style="inputStyle"
@confirm="addMyDoctorOrder"
/>
<view class="input-actions">
<u-button
@click="cancelAdd"
size="mini"
plain
:custom-style="{ marginRight: '16rpx' }"
>取消</u-button>
<u-button
@click="addMyDoctorOrder"
size="mini"
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff' }"
>确认</u-button>
</view>
</view>
<!-- 我的医嘱列表 --> <!-- 我的医嘱列表 -->
<view class="tag-container"> <view class="modern-tag-container">
<view <view
v-for="item in doctorOrderMyList" v-for="item in doctorOrderMyList"
:key="item.id" :key="item.id"
class="tag-item" class="modern-tag-item"
:class="{ 'selected': item.isSelect === 1 }" :class="{ 'is-selected': item.isSelect === 1 }"
@click="selectDoctorOrder(item, 1)" @click="selectDoctorOrder(item, 1)"
> >
<text class="tag-text">{{ item.content }}</text> <text class="tag-text">{{ item.content }}</text>
<view class="tag-action" @click.stop="deleteMyDoctorOrder(item)"> <view class="tag-action" @click.stop="deleteMyDoctorOrder(item)">
<u-icon name="close" size="14" color="#999"></u-icon> <u-icon name="close" size="20"></u-icon>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 公共医嘱 --> <!-- 公共医嘱 -->
<view class="section" v-if="doctorOrderCommonList.length > 0"> <view class="section white radius-16 p-32 shadow-sm" v-if="doctorOrderCommonList.length > 0">
<view class="section-header"> <view class="section-header m-b-24">
<d-text text="公共医嘱" className="fs-28 content-c"></d-text> <d-text text="系统公共医嘱" className="fs-30 font-bold color-title"></d-text>
</view> </view>
<view class="tag-container"> <view class="modern-tag-container">
<view <view
v-for="item in doctorOrderCommonList" v-for="item in doctorOrderCommonList"
:key="item.id" :key="item.id"
class="tag-item" class="modern-tag-item"
:class="{ 'selected': item.isSelect === 1 }" :class="{ 'is-selected': item.isSelect === 1 }"
@click="selectDoctorOrder(item, 2)" @click="selectDoctorOrder(item, 2)"
> >
<text class="tag-text">{{ item.content }}</text> <text class="tag-text">{{ item.content }}</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<view class="modal-footer flex-jus-between flex-ali-center"> <view class="modal-footer flex-row flex-jus-between flex-ali-center white shadow-up">
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleClose" @click="handleClose"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#f5f5f5', backgroundColor: '#F4F6F8',
color: '#333', color: '#2A2E35',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
border: 'none'
}" }"
>取消</u-button> >取消</u-button>
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleConfirm" @click="handleConfirm"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#6ACDBB', backgroundColor: '#00A88A',
color: '#fff', color: '#fff',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
boxShadow: '0 8rpx 20rpx rgba(0, 168, 138, 0.25)',
border: 'none'
}" }"
>确定</u-button> >确定</u-button>
</view> </view>
</view> </view>
<!-- 添加自定义医嘱弹窗 -->
<u-modal
v-model="showAddModal"
:show-cancel-button="true"
title="添加医嘱"
z-index="10090"
@confirm="addMyDoctorOrder"
@cancel="cancelAdd"
>
<view class="slot-content" style="padding: 40rpx;">
<u-input
v-model="newOrderContent"
placeholder="请输入医嘱内容"
type="text"
:border="true"
border-color="#E2E8F0"
/>
</view>
</u-modal>
</u-popup> </u-popup>
</template> </template>
<script> <script>
// 保留所有原有逻辑代码与注释
import { getDoctorOrderList, getDoctorOrderCommonList, createDoctorOrder, deleteDoctorOrder } from '@/api/reception.js'; import { getDoctorOrderList, getDoctorOrderCommonList, createDoctorOrder, deleteDoctorOrder } from '@/api/reception.js';
export default { export default {
@@ -140,15 +145,8 @@ export default {
doctorOrderCommonList: [], doctorOrderCommonList: [],
doctorOrderMyList: [], doctorOrderMyList: [],
selectDoctorOrderList: '', selectDoctorOrderList: '',
showAddInput: false, showAddModal: false,
newOrderContent: '', newOrderContent: ''
inputStyle: {
fontSize: '28rpx',
backgroundColor: '#F3F4F5',
borderRadius: '8rpx',
height: '66rpx',
padding: '8rpx 16rpx'
}
}; };
}, },
watch: { watch: {
@@ -191,7 +189,7 @@ export default {
} else { } else {
this.doctorOrderMyList = []; this.doctorOrderMyList = [];
} }
// 获取公共医嘱 // 获取公共医嘱
const commonRes = await getDoctorOrderCommonList({ const commonRes = await getDoctorOrderCommonList({
store_id: uni.getStorageSync('store_id') || 11001 store_id: uni.getStorageSync('store_id') || 11001
@@ -215,7 +213,7 @@ export default {
this.$toast('获取医嘱列表失败'); this.$toast('获取医嘱列表失败');
} }
}, },
/** /**
* 选择医嘱 * 选择医嘱
* @param {Object} item - 医嘱项 * @param {Object} item - 医嘱项
@@ -224,7 +222,7 @@ export default {
*/ */
selectDoctorOrder(item, type = 1) { selectDoctorOrder(item, type = 1) {
let arr = this.selectDoctorOrderList ? this.selectDoctorOrderList.split('').filter(Boolean) : []; let arr = this.selectDoctorOrderList ? this.selectDoctorOrderList.split('').filter(Boolean) : [];
if (type === 1) { if (type === 1) {
// 我的医嘱 // 我的医嘱
if (item.isSelect === 1) { if (item.isSelect === 1) {
@@ -248,10 +246,19 @@ export default {
item.isSelect = 1; item.isSelect = 1;
} }
} }
this.selectDoctorOrderList = arr.join(''); this.selectDoctorOrderList = arr.join('');
}, },
/**
* 打开添加医嘱弹窗
* 职责:清空旧内容,打开弹窗
*/
openAddModal() {
this.newOrderContent = '';
this.showAddModal = true;
},
/** /**
* 添加自定义医嘱 * 添加自定义医嘱
* 职责:调用接口添加我的医嘱 * 职责:调用接口添加我的医嘱
@@ -261,13 +268,13 @@ export default {
this.$toast('请输入医嘱内容'); this.$toast('请输入医嘱内容');
return; return;
} }
try { try {
const res = await createDoctorOrder(this.newOrderContent.trim()); const res = await createDoctorOrder(this.newOrderContent.trim());
if (res && (res.code === 0 || res.errcode === 0)) { if (res && (res.code === 0 || res.errcode === 0)) {
this.$toast('添加成功'); this.$toast('添加成功');
this.newOrderContent = ''; this.newOrderContent = '';
this.showAddInput = false; this.showAddModal = false;
await this.loadDoctorOrderListData(); await this.loadDoctorOrderListData();
} else { } else {
this.$toast(res.msg || res.message || '添加失败'); this.$toast(res.msg || res.message || '添加失败');
@@ -277,7 +284,7 @@ export default {
this.$toast('添加失败'); this.$toast('添加失败');
} }
}, },
/** /**
* 删除我的医嘱 * 删除我的医嘱
* @param {Object} item - 医嘱项 * @param {Object} item - 医嘱项
@@ -297,16 +304,16 @@ export default {
this.$toast('删除失败'); this.$toast('删除失败');
} }
}, },
/** /**
* 取消添加 * 取消添加
* 职责:关闭添加输入框 * 职责:关闭弹窗
*/ */
cancelAdd() { cancelAdd() {
this.showAddModal = false;
this.newOrderContent = ''; this.newOrderContent = '';
this.showAddInput = false;
}, },
/** /**
* 确认选择 * 确认选择
* 职责:触发确认事件,传递选中的医嘱文本 * 职责:触发确认事件,传递选中的医嘱文本
@@ -315,7 +322,7 @@ export default {
this.$emit('confirm', this.selectDoctorOrderList); this.$emit('confirm', this.selectDoctorOrderList);
this.handleClose(); this.handleClose();
}, },
/** /**
* 关闭弹窗 * 关闭弹窗
* 职责关闭弹窗触发input事件 * 职责关闭弹窗触发input事件
@@ -329,106 +336,112 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 使用与 index.vue 统一的设计风格与变量 */
.page-bg { background-color: #F4F6F8; }
.white { background: #fff; }
.color-title { color: #2A2E35; }
.fs-26 { font-size: 26rpx; }
.fs-30 { font-size: 30rpx; }
.fs-32 { font-size: 32rpx; }
.font-bold { font-weight: bold; }
.m-b-24 { margin-bottom: 24rpx; }
.m-t-16 { margin-top: 16rpx; }
.m-r-8 { margin-right: 8rpx; }
.m-l-16 { margin-left: 16rpx; }
.p-32 { padding: 32rpx; }
.radius-16 { border-radius: 16rpx; }
.shadow-sm { box-shadow: 0 4rpx 16rpx rgba(42, 46, 53, 0.04); }
.shadow-up { box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
.flex-row { display: flex; flex-direction: row; }
.flex-jus-sp { justify-content: space-between; }
.flex-jus-end { justify-content: flex-end; }
.flex-jus-between { justify-content: space-between; }
.flex-ali-center { align-items: center; }
.doctor-order-modal { .doctor-order-modal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #fff; overflow: hidden;
} }
.modal-header { .modal-header {
padding: 32rpx; padding: 32rpx;
text-align: center; text-align: center;
border-bottom: 1rpx solid #eee; border-bottom: 1px solid #F0F2F5;
} }
.modal-content { .modal-content {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 32rpx; padding: 32rpx;
/* 底部防遮挡边距 */
padding-bottom: 24rpx;
} }
.section { .add-btn-modern {
margin-bottom: 32rpx;
}
.section-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; padding: 8rpx 20rpx;
margin-bottom: 24rpx; background: #E8F6F4;
border-radius: 32rpx;
.add-btn { cursor: pointer;
display: flex;
align-items: center; .add-text {
gap: 8rpx; font-size: 24rpx;
padding: 8rpx 16rpx; font-weight: bold;
border: 1rpx dashed #6ACDBB; color: #00A88A;
border-radius: 32rpx;
.add-text {
font-size: 24rpx;
color: #6ACDBB;
}
} }
} }
.add-input-box { .modern-tag-container {
margin-bottom: 24rpx;
padding: 16rpx;
background-color: #f9fafb;
border-radius: 8rpx;
.input-actions {
display: flex;
justify-content: flex-end;
margin-top: 16rpx;
}
}
.tag-container {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 16rpx; gap: 20rpx;
} }
.tag-item { .modern-tag-item {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
padding: 12rpx 24rpx; padding: 12rpx 24rpx;
background-color: #f9fafb; background-color: #F7F8FA;
border: 1rpx solid #e5e7eb; border: 1px solid #E2E8F0;
border-radius: 9999rpx; border-radius: 36rpx;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
&.selected { &.is-selected {
background-color: #eff6ff; background-color: #E8F6F4;
border-color: #6ACDBB; border-color: #00A88A;
.tag-text { .tag-text {
color: #6ACDBB; color: #00A88A;
font-weight: 500; font-weight: bold;
}
.tag-action {
color: #00A88A;
} }
} }
.tag-text { .tag-text {
font-size: 28rpx; font-size: 26rpx;
color: #4b5563; color: #4b5563;
} }
.tag-action { .tag-action {
margin-left: 8rpx; margin-left: 12rpx;
display: flex; display: flex;
align-items: center; align-items: center;
color: #8A92A3;
} }
} }
.modal-footer { .modal-footer {
display: flex; display: flex;
flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 32rpx; padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #eee; background-color: #fff;
} }
</style> </style>

View File

@@ -1,95 +1,100 @@
<template> <template>
<u-popup <u-popup
v-model="show" v-model="show"
mode="bottom" mode="bottom"
:closeable="true" :closeable="true"
@close="handleClose" @close="handleClose"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="false" :mask-close-able="false"
z-index="10078" z-index="10078"
height="80%" height="80%"
> >
<view class="simple-product-modal"> <view class="simple-product-modal page-bg">
<view class="modal-header"> <view class="modal-header white">
<d-text :text="getModalTitle()" className="fs-32 main-c"></d-text> <d-text :text="getModalTitle()" className="fs-32 font-bold color-title"></d-text>
</view> </view>
<view class="modal-content"> <view class="modal-content">
<!-- 搜索框 --> <!-- 搜索框 -->
<view class="search-box"> <view class="search-box m-b-24">
<u-search <u-search
v-model="searchKey" v-model="searchKey"
placeholder="请输入产品名称搜索" placeholder="请输入产品名称搜索"
@input="handleSearch" @input="handleSearch"
@custom="searchKey = ''; handleSearch()" @custom="searchKey = ''; handleSearch()"
:show-action="false" :show-action="false"
bg-color="#fff"
/> />
</view> </view>
<!-- 产品列表 --> <!-- 产品列表 -->
<view class="product-list" v-if="productList.length > 0"> <view class="product-list" v-if="productList.length > 0">
<view <view
class="product-item white b-r-8 p-32 m-t-2" class="medicine-card"
v-for="item in productList" v-for="item in productList"
:key="item.id" :key="item.id"
> >
<view class="product-content flex-row"> <view class="flex-row">
<image <image
v-if="(item.drug && item.drug.image) || item.image" v-if="(item.drug && item.drug.image) || item.image"
:src="(item.drug && item.drug.image) || item.image" :src="(item.drug && item.drug.image) || item.image"
class="product-image" class="drug-image-modern"
mode="aspectFill" mode="aspectFill"
></image> ></image>
<view class="product-info-wrapper flex-1"> <view class="flex-1 m-l-24 flex-col">
<view class="product-header flex-row flex-jus-sp flex-ali-center"> <view class="flex-row flex-jus-sp flex-ali-start">
<d-text :text="item.drug.drug_name || item.drug.name" className="fs-3 color0 bold"></d-text> <text class="fs-30 font-bold color-title line-clamp-2 w-70">{{item.drug.drug_name || item.drug.name}}</text>
<d-text :text="`¥${parseFloat(item.price || 0).toFixed(2)}`" className="fs-3 error-c"></d-text> <text class="fs-32 font-bold color-price">{{parseFloat(item.price || 0).toFixed(2)}}</text>
</view> </view>
<view class="product-info m-t-16"> <view class="flex-row flex-jus-sp flex-ali-center m-t-12">
<d-text :text="`规格:${item.drug.specification || '--'}`" className="fs-24 color9"></d-text> <text class="fs-24 color-sub">规格{{item.drug.specification || '--'}}</text>
</view> </view>
<view class="product-action m-t-16">
<u-button <view class="product-action m-t-24">
size="mini" <view class="action-btn-ghost primary" @click.stop="handleSelectProduct(item)">
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff' }" <u-icon name="plus" size="24" class="m-r-8"></u-icon> 选用此产品
@click.stop="handleSelectProduct(item)" </view>
>
选择
</u-button>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view class="empty-state" v-else> <view class="empty-state" v-else>
<d-empty text="暂无产品"></d-empty> <d-empty text="暂无产品"></d-empty>
</view> </view>
</view> </view>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<view class="modal-footer flex-jus-sp flex-ali-center"> <view class="modal-footer flex-row flex-jus-between flex-ali-center white shadow-up">
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleClose" @click="handleClose"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#f5f5f5', backgroundColor: '#F4F6F8',
color: '#333', color: '#2A2E35',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
border: 'none'
}" }"
>取消</u-button> >取消</u-button>
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleClose" @click="handleClose"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#6ACDBB', backgroundColor: '#00A88A',
color: '#fff', color: '#fff',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
boxShadow: '0 8rpx 20rpx rgba(0, 168, 138, 0.25)',
border: 'none'
}" }"
>确定</u-button> >确定</u-button>
</view> </view>
@@ -98,6 +103,7 @@
</template> </template>
<script> <script>
// 保留所有原有逻辑代码与注释
import { getProductListDoctorReception } from '@/api/reception.js'; import { getProductListDoctorReception } from '@/api/reception.js';
export default { export default {
@@ -161,7 +167,7 @@ export default {
}; };
return titles[this.productType] || '选择产品'; return titles[this.productType] || '选择产品';
}, },
/** /**
* 加载产品列表 * 加载产品列表
* @param {string} keyword - 搜索关键词 * @param {string} keyword - 搜索关键词
@@ -176,9 +182,9 @@ export default {
type: this.productType, type: this.productType,
name: keyword name: keyword
}); });
if (res && (res.code === 0 || res.errcode === 0)) { if (res && (res.code === 0 || res.errcode === 0)) {
this.productList = res.data || res.result || []; this.productList = res.result || [];
} else { } else {
this.productList = []; this.productList = [];
} }
@@ -190,7 +196,7 @@ export default {
this.loading = false; this.loading = false;
} }
}, },
/** /**
* 搜索产品 * 搜索产品
* 职责:防抖搜索,调用加载产品列表 * 职责:防抖搜索,调用加载产品列表
@@ -201,7 +207,7 @@ export default {
this.loadProductList(this.searchKey); this.loadProductList(this.searchKey);
}, 300); }, 300);
}, },
/** /**
* 选择产品 * 选择产品
* @param {Object} product - 产品数据 * @param {Object} product - 产品数据
@@ -211,7 +217,7 @@ export default {
this.$emit('select', product); this.$emit('select', product);
this.handleClose(); this.handleClose();
}, },
/** /**
* 关闭弹窗 * 关闭弹窗
* 职责关闭弹窗触发input事件 * 职责关闭弹窗触发input事件
@@ -225,127 +231,123 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 使用与 index.vue 统一的设计风格与变量 */
.page-bg { background-color: #F4F6F8; }
.white { background: #fff; }
.color-title { color: #2A2E35; }
.color-sub { color: #8A92A3; }
.color-price { color: #F53F3F; }
.fs-24 { font-size: 24rpx; }
.fs-30 { font-size: 30rpx; }
.fs-32 { font-size: 32rpx; }
.font-bold { font-weight: bold; }
.m-b-24 { margin-bottom: 24rpx; }
.m-t-12 { margin-top: 12rpx; }
.m-t-24 { margin-top: 24rpx; }
.m-l-24 { margin-left: 24rpx; }
.m-r-8 { margin-right: 8rpx; }
.shadow-up { box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
.flex-row { display: flex; flex-direction: row; }
.flex-col { display: flex; flex-direction: column; }
.flex-jus-sp { justify-content: space-between; }
.flex-jus-between { justify-content: space-between; }
.flex-ali-center { align-items: center; }
.flex-ali-start { align-items: flex-start; }
.flex-1 { flex: 1; }
.simple-product-modal { .simple-product-modal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #fff; overflow: hidden;
} }
.modal-header { .modal-header {
padding: 32rpx; padding: 32rpx;
text-align: center; text-align: center;
border-bottom: 1rpx solid #eee; border-bottom: 1px solid #F0F2F5;
} }
.modal-content { .modal-content {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 32rpx; padding: 32rpx;
display: flex;
flex-direction: column;
} }
.search-box { /* 底部防遮挡边距 */
margin-bottom: 32rpx;
}
.product-list { .product-list {
padding-bottom: 32rpx; padding-bottom: 24rpx;
} }
.product-item { /* 底部防遮挡边距 */
margin-bottom: 16rpx; .product-list {
padding-bottom: 120rpx;
} }
.product-content { /* 现代卡片样式 */
gap: 24rpx; .medicine-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(42, 46, 53, 0.04);
position: relative;
overflow: hidden;
} }
.product-image { .drug-image-modern {
width: 120rpx; width: 140rpx;
height: 120rpx; height: 140rpx;
border-radius: 8rpx; border-radius: 12rpx;
background-color: #F7F8FA;
flex-shrink: 0; flex-shrink: 0;
background-color: #f5f5f5; border: 1px solid #F0F2F5;
} }
.product-info-wrapper { .line-clamp-2 {
min-width: 0; display: -webkit-box;
flex: 1; -webkit-box-orient: vertical;
} -webkit-line-clamp: 2;
overflow: hidden;
.product-header {
margin-bottom: 16rpx;
}
.product-info {
margin-bottom: 16rpx;
} }
.w-70 { width: 70%; }
.product-action { .product-action {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
.empty-state { .action-btn-ghost {
//padding: 100rpx 0; padding: 8rpx 24rpx;
text-align: center; border-radius: 30rpx;
} font-size: 24rpx;
.white {
background: #fff;
}
.b-r-8 {
border-radius: 8rpx;
}
.p-32 {
padding: 32rpx;
}
.m-t-2 {
margin-top: 16rpx;
}
.m-t-16 {
margin-top: 16rpx;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-jus-sp {
justify-content: space-between;
}
.flex-ali-center {
align-items: center;
}
.color0 {
color: #000;
}
.color9 {
color: #999;
}
.bold {
font-weight: bold; font-weight: bold;
display: flex;
align-items: center;
&.primary {
color: #00A88A;
background: #E8F6F4;
}
}
.empty-state {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 100rpx 0;
text-align: center;
} }
.modal-footer { .modal-footer {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 32rpx; align-items: center;
border-top: 1rpx solid #eee; padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
background-color: #fff; background-color: #fff;
position: sticky;
bottom: 0;
z-index: 10;
} }
</style> </style>

View File

@@ -1,107 +1,104 @@
<template> <template>
<u-popup <u-popup
v-model="show" v-model="show"
mode="bottom" mode="bottom"
:closeable="true" :closeable="true"
@close="handleClose" @close="handleClose"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="false" :mask-close-able="false"
z-index="10078" z-index="10078"
height="80%" height="80%"
> >
<view class="western-medicine-modal"> <view class="western-medicine-modal page-bg">
<view class="modal-header"> <view class="modal-header white">
<d-text text="选择西药" className="fs-32 main-c"></d-text> <d-text text="选择西药" className="fs-32 font-bold color-title"></d-text>
</view> </view>
<view class="modal-content"> <view class="modal-content">
<!-- 搜索框 --> <!-- 搜索框 -->
<view class="search-box"> <view class="search-box m-b-24">
<u-search <u-search
v-model="searchKey" v-model="searchKey"
placeholder="请输入药品名称搜索" placeholder="请输入药品名称搜索"
@input="handleSearch" @input="handleSearch"
@custom="searchKey = ''; handleSearch()" @custom="searchKey = ''; handleSearch()"
:show-action="false" :show-action="false"
bg-color="#fff"
/> />
</view> </view>
<!-- 药品列表 --> <!-- 药品列表 -->
<view class="drug-list" v-if="drugList.length > 0"> <view class="drug-list" v-if="drugList.length > 0">
<view <view
class="drug-card white b-r-8 p-32 m-t-2" class="medicine-card"
v-for="(item, index) in drugList" v-for="(item, index) in drugList"
:key="index" :key="index"
:class="{ selected: isSelected(item) }" :class="{ 'is-selected': isSelected(item) }"
@click="!isSelected(item) && handleSelectDrug(item)" @click="!isSelected(item) && handleSelectDrug(item)"
> >
<view class="drug-content flex-row"> <view class="flex-row">
<image <image
v-if="getDrugImage(item)" v-if="getDrugImage(item)"
:src="getDrugImage(item)" :src="getDrugImage(item)"
class="drug-image" class="drug-image-modern"
mode="aspectFill" mode="aspectFill"
></image> ></image>
<view class="drug-info-wrapper flex-1"> <view class="flex-1 m-l-24 flex-col">
<view class="drug-header flex-row flex-jus-sp flex-ali-center"> <view class="flex-row flex-jus-sp flex-ali-start">
<d-text <text class="fs-30 font-bold color-title line-clamp-2 w-70">{{item.drug.drug_name || item.drug.name}}</text>
:text="`${index + 1}、${item.drug.drug_name || item.drug.name}`" <text class="fs-32 font-bold color-price">{{parseFloat(item.price || 0).toFixed(2)}}</text>
className="fs-3 color0 bold"
></d-text>
<d-text
:text="`¥${parseFloat(item.price || 0).toFixed(2)}`"
className="fs-3 error-c"
></d-text>
</view> </view>
<view class="drug-info m-t-16 flex-row flex-ali-center flex-jus-sp"> <view class="flex-row flex-jus-sp flex-ali-center m-t-12">
<d-text <text class="fs-24 color-sub">规格{{item.drug.specification || '--'}}</text>
:text="`规格:${item.drug.specification || '--'}`" </view>
className="fs-24 color9" <view class="drug-status m-t-24">
></d-text> <view v-if="isSelected(item)" class="action-btn-ghost disabled">
已添加处方
</view>
<view v-else class="action-btn-ghost primary">
<u-icon name="plus" size="24" class="m-r-8"></u-icon> 选用此药
</view>
</view> </view>
</view>
<view class="drug-status">
<u-button
size="mini"
:disabled="isSelected(item)"
:custom-style="isSelected(item) ? selectedButtonStyle : addButtonStyle"
@click.stop="handleSelectDrug(item)"
>
{{ isSelected(item) ? '已选' : '添加' }}
</u-button>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view class="empty-state" v-else> <view class="empty-state" v-else>
<d-empty text="暂无药品"></d-empty> <d-empty text="暂无药品"></d-empty>
</view> </view>
</view> </view>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<view class="modal-footer flex-jus-sp flex-ali-center"> <view class="modal-footer flex-row flex-jus-between flex-ali-center white shadow-up">
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleClose" @click="handleClose"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#f5f5f5', backgroundColor: '#F4F6F8',
color: '#333', color: '#2A2E35',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
border: 'none'
}" }"
>取消</u-button> >取消</u-button>
<u-button <u-button
:throttle-time="0" :throttle-time="0"
@click="handleClose" @click="handleClose"
shape="circle" shape="circle"
:custom-style="{ :custom-style="{
backgroundColor: '#6ACDBB', backgroundColor: '#00A88A',
color: '#fff', color: '#fff',
height: '86rpx', height: '88rpx',
width: '320rpx' width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
boxShadow: '0 8rpx 20rpx rgba(0, 168, 138, 0.25)',
border: 'none'
}" }"
>确定</u-button> >确定</u-button>
</view> </view>
@@ -110,6 +107,7 @@
</template> </template>
<script> <script>
// 保留所有原有逻辑代码与注释
import { getProductListDoctorReception } from '@/api/reception.js'; import { getProductListDoctorReception } from '@/api/reception.js';
export default { export default {
@@ -136,16 +134,7 @@ export default {
searchKey: '', searchKey: '',
drugList: [], drugList: [],
loading: false, loading: false,
searchTimer: null, searchTimer: null
addButtonStyle: {
backgroundColor: '#6ACDBB',
color: '#fff'
},
selectedButtonStyle: {
backgroundColor: '#F5F7FA',
color: '#6ACDBB',
borderColor: '#6ACDBB'
}
}; };
}, },
watch: { watch: {
@@ -176,7 +165,7 @@ export default {
type: 2, // 西药 type: 2, // 西药
name: keyword name: keyword
}); });
if (res && (res.code === 0 || res.errcode === 0)) { if (res && (res.code === 0 || res.errcode === 0)) {
this.drugList = res.data || res.result || []; this.drugList = res.data || res.result || [];
} else { } else {
@@ -190,7 +179,7 @@ export default {
this.loading = false; this.loading = false;
} }
}, },
/** /**
* 搜索药品 * 搜索药品
* 职责:防抖搜索,调用加载药品列表 * 职责:防抖搜索,调用加载药品列表
@@ -201,7 +190,7 @@ export default {
this.loadDrugList(this.searchKey); this.loadDrugList(this.searchKey);
}, 300); }, 300);
}, },
/** /**
* 获取药品图片 * 获取药品图片
* @param {Object} item - 药品数据 * @param {Object} item - 药品数据
@@ -216,7 +205,7 @@ export default {
} }
return ''; return '';
}, },
/** /**
* 判断药品是否已在当前处方中 * 判断药品是否已在当前处方中
* @param {Object} drug * @param {Object} drug
@@ -227,7 +216,7 @@ export default {
const drugId = drug.drug_id || (drug.drug && drug.drug.id) || drug.id; const drugId = drug.drug_id || (drug.drug && drug.drug.id) || drug.id;
return this.currentDrugs.some(item => (item.id || item.drug_id) === drugId); return this.currentDrugs.some(item => (item.id || item.drug_id) === drugId);
}, },
/** /**
* 选择药品 * 选择药品
* @param {Object} drug - 药品数据 * @param {Object} drug - 药品数据
@@ -240,7 +229,7 @@ export default {
this.$emit('select', drug); this.$emit('select', drug);
this.handleClose(); this.handleClose();
}, },
/** /**
* 关闭弹窗 * 关闭弹窗
* 职责关闭弹窗触发input事件 * 职责关闭弹窗触发input事件
@@ -254,132 +243,137 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 使用与 index.vue 统一的设计风格与变量 */
.page-bg { background-color: #F4F6F8; }
.white { background: #fff; }
.color-title { color: #2A2E35; }
.color-sub { color: #8A92A3; }
.color-price { color: #F53F3F; }
.fs-24 { font-size: 24rpx; }
.fs-30 { font-size: 30rpx; }
.fs-32 { font-size: 32rpx; }
.font-bold { font-weight: bold; }
.m-b-24 { margin-bottom: 24rpx; }
.m-t-12 { margin-top: 12rpx; }
.m-t-24 { margin-top: 24rpx; }
.m-l-24 { margin-left: 24rpx; }
.m-r-8 { margin-right: 8rpx; }
.shadow-up { box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
.flex-row { display: flex; flex-direction: row; }
.flex-col { display: flex; flex-direction: column; }
.flex-jus-sp { justify-content: space-between; }
.flex-jus-between { justify-content: space-between; }
.flex-ali-center { align-items: center; }
.flex-ali-start { align-items: flex-start; }
.flex-1 { flex: 1; }
.western-medicine-modal { .western-medicine-modal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #fff; overflow: hidden;
} }
.modal-header { .modal-header {
padding: 32rpx; padding: 32rpx;
text-align: center; text-align: center;
border-bottom: 1rpx solid #eee; border-bottom: 1px solid #F0F2F5;
} }
.modal-content { .modal-content {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 32rpx; padding: 32rpx;
display: flex;
flex-direction: column;
} }
.search-box { /* 底部防遮挡边距 */
margin-bottom: 32rpx;
}
.drug-list { .drug-list {
padding-bottom: 32rpx; padding-bottom: 24rpx;
} }
.drug-item { /* 底部防遮挡边距 */
margin-bottom: 16rpx; .drug-list {
padding-bottom: 120rpx;
} }
.drug-content { /* 现代卡片样式 */
gap: 24rpx; .medicine-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(42, 46, 53, 0.04);
position: relative;
overflow: hidden;
border: 2rpx solid transparent;
transition: all 0.2s;
&.is-selected {
border-color: #E2E8F0;
background-color: #F8FAFC;
opacity: 0.8;
}
} }
.drug-image { .drug-image-modern {
width: 120rpx; width: 140rpx;
height: 120rpx; height: 140rpx;
border-radius: 8rpx; border-radius: 12rpx;
background-color: #F7F8FA;
flex-shrink: 0; flex-shrink: 0;
background-color: #f5f5f5; border: 1px solid #F0F2F5;
} }
.drug-info-wrapper { .line-clamp-2 {
min-width: 0; display: -webkit-box;
} -webkit-box-orient: vertical;
-webkit-line-clamp: 2;
.drug-header { overflow: hidden;
margin-bottom: 16rpx;
}
.drug-info {
margin-bottom: 16rpx;
} }
.w-70 { width: 70%; }
.drug-status { .drug-status {
display: flex; display: flex;
align-items: center;
justify-content: flex-end; justify-content: flex-end;
} }
.empty-state { .action-btn-ghost {
padding: 100rpx 0; padding: 8rpx 24rpx;
align-items: center; border-radius: 30rpx;
text-align: center; font-size: 24rpx;
}
.white {
background: #fff;
}
.b-r-8 {
border-radius: 8rpx;
}
.p-32 {
padding: 32rpx;
}
.m-t-2 {
margin-top: 16rpx;
}
.m-t-16 {
margin-top: 16rpx;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-jus-sp {
justify-content: space-between;
}
.flex-ali-center {
align-items: center;
}
.flex-1 {
flex: 1;
}
.color0 {
color: #000;
}
.color9 {
color: #999;
}
.bold {
font-weight: bold; font-weight: bold;
display: flex;
align-items: center;
&.primary {
color: #00A88A;
background: #E8F6F4;
}
&.disabled {
color: #8A92A3;
background: #F4F6F8;
border: 1px solid #E2E8F0;
}
}
.empty-state {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 100rpx 0;
text-align: center;
} }
.modal-footer { .modal-footer {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 32rpx; align-items: center;
border-top: 1rpx solid #eee; padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
background-color: #fff; background-color: #fff;
position: sticky;
bottom: 0;
z-index: 10;
} }
</style> </style>

View File

@@ -1,137 +1,175 @@
<template> <template>
<u-popup <u-popup
v-model="show" v-model="show"
mode="bottom" mode="bottom"
:closeable="true" :closeable="true"
@close="handleClose" @close="handleClose"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="false" :mask-close-able="false"
z-index="10078" z-index="10078"
height="80%" height="80%"
> >
<view class="western-usage-modal"> <view class="western-usage-modal page-bg">
<view class="modal-header"> <view class="modal-header white">
<d-text text="用法用量" className="fs-32 main-c"></d-text> <d-text text="用法用量" className="fs-32 font-bold color-title"></d-text>
</view> </view>
<view class="modal-content"> <view class="modal-content">
<view class="drug-info white b-r-8 p-32 m-t-2" v-if="drug"> <view class="medicine-card m-b-24" v-if="drug">
<view class="info-item"> <view class="flex-row flex-ali-start">
<d-text text="名称:" className="fs-3 color9"></d-text> <view class="info-icon m-r-16">
<d-text :text="drug.drug_name" className="fs-3 color0"></d-text> <u-icon name="info-circle-fill" size="36" color="#00A88A"></u-icon>
</view> </view>
<view class="info-item m-t-16"> <view class="flex-col">
<d-text text="规格:" className="fs-3 color9"></d-text> <d-text :text="drug.drug_name" className="fs-32 font-bold color-title m-b-8"></d-text>
<d-text :text="drug.specification || '--'" className="fs-3 color0"></d-text> <d-text :text="`规格:${drug.specification || '--'}`" className="fs-26 color-sub"></d-text>
</view>
</view> </view>
</view> </view>
<!-- 药品数量 --> <!-- 表单区域使用统一现代化表单卡片 -->
<view class="usage-item white b-r-8 p-32 m-t-2"> <view class="form-card">
<view class="item-label"> <!-- 药品数量 -->
<d-text text="药品数量" className="fs-3 main-c"></d-text> <view class="form-item border-b">
<view class="item-label">
<d-text text="药品数量" className="fs-30 font-bold color-title"></d-text>
</view>
<view class="item-content flex-row flex-ali-center">
<u-number-box
v-model="usageData.select_number"
:min="1"
:max="(drug && drug.stock) || 999"
:input-width="80"
:input-height="60"
bg-color="#F7F8FA"
/>
<d-text text="盒" className="fs-28 color-sub m-l-16"></d-text>
</view>
</view> </view>
<view class="item-content">
<u-number-box <!-- 用法 -->
v-model="usageData.select_number" <view class="form-item border-b">
:min="1" <view class="item-label">
:max="(drug && drug.stock) || 999" <d-text text="给药途径" className="fs-30 font-bold color-title"></d-text>
/> </view>
<d-text text="盒" className="fs-3 color9 m-l-16"></d-text> <view class="item-content">
<picker
mode="selector"
:range="drugUseList.drug_use_type || []"
range-key="name"
:value="usageData.type_id && drugUseList.drug_use_type ? drugUseList.drug_use_type.findIndex(item => item.id === usageData.type_id) : 0"
@change="handleChangeUsageType"
>
<view class="modern-picker-view">
<text class="fs-28 color-title">{{ (usageData.use_type && usageData.use_type.name) || '请选择用法' }}</text>
<u-icon name="arrow-right" size="24" color="#B0B6C2"></u-icon>
</view>
</picker>
</view>
</view> </view>
</view>
<!-- 频次 -->
<!-- 用法 --> <view class="form-item border-b">
<view class="usage-item white b-r-8 p-32 m-t-2"> <view class="item-label">
<view class="item-label"> <d-text text="用药频次" className="fs-30 font-bold color-title"></d-text>
<d-text text="用法" className="fs-3 main-c"></d-text> </view>
<view class="item-content">
<picker
mode="selector"
:range="drugUseList.drug_use_frequency || []"
range-key="name"
:value="usageData.frequency_id && drugUseList.drug_use_frequency ? drugUseList.drug_use_frequency.findIndex(item => item.id === usageData.frequency_id) : 0"
@change="handleChangeFrequency"
>
<view class="modern-picker-view">
<text class="fs-28 color-title">{{ (usageData.use_frequency && usageData.use_frequency.name) || '请选择频次' }}</text>
<u-icon name="arrow-right" size="24" color="#B0B6C2"></u-icon>
</view>
</picker>
</view>
</view> </view>
<view class="item-content">
<picker <!-- 时间 -->
mode="selector" <view class="form-item border-b">
:range="drugUseList.drug_use_type || []" <view class="item-label">
range-key="name" <d-text text="用药时间" className="fs-30 font-bold color-title"></d-text>
:value="usageData.type_id && drugUseList.drug_use_type ? drugUseList.drug_use_type.findIndex(item => item.id === usageData.type_id) : 0" </view>
@change="handleChangeUsageType" <view class="item-content">
> <picker
<view class="picker-view"> mode="selector"
{{ (usageData.use_type && usageData.use_type.name) || '请选择' }} :range="drugUseList.drug_time || []"
</view> range-key="name"
</picker> :value="usageData.time_id && drugUseList.drug_time ? drugUseList.drug_time.findIndex(item => item.id === usageData.time_id) : 0"
@change="handleChangeTime"
>
<view class="modern-picker-view">
<text class="fs-28 color-title">{{ (usageData.use_num && usageData.use_num.name) || '请选择时间' }}</text>
<u-icon name="arrow-right" size="24" color="#B0B6C2"></u-icon>
</view>
</picker>
</view>
</view> </view>
</view>
<!-- 单位与单次用量 -->
<!-- 频次 --> <view class="form-item">
<view class="usage-item white b-r-8 p-32 m-t-2"> <view class="item-label">
<view class="item-label"> <d-text text="单次用量" className="fs-30 font-bold color-title"></d-text>
<d-text text="频次" className="fs-3 main-c"></d-text> </view>
</view> <view class="item-content flex-row flex-ali-center flex-jus-end">
<view class="item-content"> <u-number-box
<picker v-model="usageData.number"
mode="selector" :min="1"
:range="drugUseList.drug_use_frequency || []" :input-width="80"
range-key="name" :input-height="60"
:value="usageData.frequency_id && drugUseList.drug_use_frequency ? drugUseList.drug_use_frequency.findIndex(item => item.id === usageData.frequency_id) : 0" bg-color="#F7F8FA"
@change="handleChangeFrequency" />
> <picker
<view class="picker-view"> mode="selector"
{{ (usageData.use_frequency && usageData.use_frequency.name) || '请选择' }} :range="drugUseList.drug_unit || []"
</view> range-key="name"
</picker> :value="usageData.unit_id && drugUseList.drug_unit ? drugUseList.drug_unit.findIndex(item => item.id === usageData.unit_id) : 0"
</view> @change="handleChangeUnit"
</view> >
<view class="modern-picker-view m-l-16" style="min-width: 140rpx;">
<!-- 时间 --> <text class="fs-28 color-title">{{ (usageData.unit && usageData.unit.name) || '单位' }}</text>
<view class="usage-item white b-r-8 p-32 m-t-2"> <u-icon name="arrow-right" size="24" color="#B0B6C2"></u-icon>
<view class="item-label"> </view>
<d-text text="时间" className="fs-3 main-c"></d-text> </picker>
</view> </view>
<view class="item-content">
<picker
mode="selector"
:range="drugUseList.drug_time || []"
range-key="name"
:value="usageData.time_id && drugUseList.drug_time ? drugUseList.drug_time.findIndex(item => item.id === usageData.time_id) : 0"
@change="handleChangeTime"
>
<view class="picker-view">
{{ (usageData.use_num && usageData.use_num.name) || '请选择' }}
</view>
</picker>
</view>
</view>
<!-- 单位 -->
<view class="usage-item white b-r-8 p-32 m-t-2">
<view class="item-label">
<d-text text="每次用量" className="fs-3 main-c"></d-text>
</view>
<view class="item-content">
<u-number-box v-model="usageData.number" :min="1" />
<picker
mode="selector"
:range="drugUseList.drug_unit || []"
range-key="name"
:value="usageData.unit_id && drugUseList.drug_unit ? drugUseList.drug_unit.findIndex(item => item.id === usageData.unit_id) : 0"
@change="handleChangeUnit"
>
<view class="picker-view m-l-16">
{{ (usageData.unit && usageData.unit.name) || '请选择' }}
</view>
</picker>
</view> </view>
</view> </view>
</view> </view>
<!-- 底部按钮 --> <!-- 底部按钮补充了 flex-row 以修复上下排列的问题 -->
<view class="modal-footer"> <view class="modal-footer flex-row flex-jus-between flex-ali-center white shadow-up">
<u-button <u-button
@click="handleClose" :throttle-time="0"
:custom-style="{ backgroundColor: '#f5f5f5', color: '#333' }" @click="handleClose"
shape="circle"
:custom-style="{
backgroundColor: '#F4F6F8',
color: '#2A2E35',
height: '88rpx',
width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
border: 'none'
}"
>取消</u-button> >取消</u-button>
<u-button <u-button
@click="handleConfirm" :throttle-time="0"
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff' }" @click="handleConfirm"
shape="circle"
:custom-style="{
backgroundColor: '#00A88A',
color: '#fff',
height: '88rpx',
width: '320rpx',
fontWeight: 'bold',
fontSize: '30rpx',
boxShadow: '0 8rpx 20rpx rgba(0, 168, 138, 0.25)',
border: 'none'
}"
>确定</u-button> >确定</u-button>
</view> </view>
</view> </view>
@@ -139,6 +177,7 @@
</template> </template>
<script> <script>
// 保留所有原有逻辑代码与注释
import { getDrugUseList, getDrugUseWithDefault } from '@/api/reception.js'; import { getDrugUseList, getDrugUseWithDefault } from '@/api/reception.js';
export default { export default {
@@ -217,7 +256,7 @@ export default {
}; };
} }
}, },
/** /**
* 加载药品使用方式列表 * 加载药品使用方式列表
* 职责:调用接口获取药品使用方式列表 * 职责:调用接口获取药品使用方式列表
@@ -268,10 +307,10 @@ export default {
} }
const hasOptions = const hasOptions =
(lists.drug_use_type || []).length || (lists.drug_use_type || []).length ||
(lists.drug_use_frequency || []).length || (lists.drug_use_frequency || []).length ||
(lists.drug_time || []).length || (lists.drug_time || []).length ||
(lists.drug_unit || []).length; (lists.drug_unit || []).length;
if (!hasOptions) { if (!hasOptions) {
this.$toast('暂无用法用量配置,请联系管理员'); this.$toast('暂无用法用量配置,请联系管理员');
} }
@@ -280,7 +319,7 @@ export default {
this.$toast('获取用法用量失败'); this.$toast('获取用法用量失败');
} }
}, },
/** /**
* 改变用法类型 * 改变用法类型
* @param {Object} e - 事件对象 * @param {Object} e - 事件对象
@@ -294,7 +333,7 @@ export default {
this.usageData.use_type = item; this.usageData.use_type = item;
} }
}, },
/** /**
* 改变频次 * 改变频次
* @param {Object} e - 事件对象 * @param {Object} e - 事件对象
@@ -308,7 +347,7 @@ export default {
this.usageData.use_frequency = item; this.usageData.use_frequency = item;
} }
}, },
/** /**
* 改变时间 * 改变时间
* @param {Object} e - 事件对象 * @param {Object} e - 事件对象
@@ -322,7 +361,7 @@ export default {
this.usageData.use_num = item; this.usageData.use_num = item;
} }
}, },
/** /**
* 改变单位 * 改变单位
* @param {Object} e - 事件对象 * @param {Object} e - 事件对象
@@ -336,7 +375,7 @@ export default {
this.usageData.unit = item; this.usageData.unit = item;
} }
}, },
/** /**
* 确认 * 确认
* 职责触发confirm事件传递用法用量数据 * 职责触发confirm事件传递用法用量数据
@@ -345,7 +384,7 @@ export default {
this.$emit('confirm', { ...this.usageData }); this.$emit('confirm', { ...this.usageData });
this.handleClose(); this.handleClose();
}, },
/** /**
* 关闭弹窗 * 关闭弹窗
* 职责关闭弹窗触发input事件 * 职责关闭弹窗触发input事件
@@ -359,97 +398,95 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 使用与 index.vue 统一的设计风格与变量 */
.page-bg { background-color: #F4F6F8; }
.white { background: #fff; }
.color-title { color: #2A2E35; }
.color-sub { color: #8A92A3; }
.fs-26 { font-size: 26rpx; }
.fs-28 { font-size: 28rpx; }
.fs-30 { font-size: 30rpx; }
.fs-32 { font-size: 32rpx; }
.font-bold { font-weight: bold; }
.m-b-8 { margin-bottom: 8rpx; }
.m-b-24 { margin-bottom: 24rpx; }
.m-r-16 { margin-right: 16rpx; }
.m-l-16 { margin-left: 16rpx; }
.border-b { border-bottom: 1px dashed #E2E8F0; }
.shadow-up { box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
.flex-row { display: flex; flex-direction: row; }
.flex-col { display: flex; flex-direction: column; }
.flex-jus-sp { justify-content: space-between; }
.flex-jus-end { justify-content: flex-end; }
.flex-jus-between { justify-content: space-between; }
.flex-ali-center { align-items: center; }
.flex-ali-start { align-items: flex-start; }
.western-usage-modal { .western-usage-modal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #fff; overflow: hidden;
} }
.modal-header { .modal-header {
padding: 32rpx; padding: 32rpx;
text-align: center; text-align: center;
border-bottom: 1rpx solid #eee; border-bottom: 1px solid #F0F2F5;
} }
.modal-content { .modal-content {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 32rpx; padding: 32rpx;
/* 底部防遮挡边距 */
padding-bottom: 24rpx;
} }
.drug-info { /* 顶部药品提示卡片 */
margin-bottom: 16rpx; .medicine-card {
background: #E8F6F4;
border-radius: 16rpx;
padding: 24rpx;
border: 1px solid #BFE6DF;
}
.info-icon {
margin-top: 4rpx;
} }
.info-item { /* 统一表单卡片 */
.form-card {
background: #fff;
border-radius: 16rpx;
padding: 0 32rpx;
box-shadow: 0 4rpx 16rpx rgba(42, 46, 53, 0.04);
}
.form-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 0;
}
.modern-picker-view {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16rpx; justify-content: space-between;
} padding: 12rpx 24rpx;
background: #F7F8FA;
.usage-item {
margin-bottom: 16rpx;
}
.item-label {
margin-bottom: 16rpx;
}
.item-content {
display: flex;
align-items: center;
gap: 16rpx;
}
.picker-view {
padding: 16rpx;
background: #f9f9f9;
border-radius: 8rpx; border-radius: 8rpx;
min-width: 200rpx; min-width: 240rpx;
text-align: center;
} }
/* 彻底解决底部按钮上下排列,显式开启 flex 布局 */
.modal-footer { .modal-footer {
display: flex; display: flex;
gap: 24rpx; flex-direction: row;
padding: 32rpx; justify-content: space-between;
border-top: 1rpx solid #eee; align-items: center;
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
.u-button { background-color: #fff;
flex: 1;
}
} }
</style>
.white {
background: #fff;
}
.b-r-8 {
border-radius: 8rpx;
}
.p-32 {
padding: 32rpx;
}
.m-t-2 {
margin-top: 16rpx;
}
.m-t-16 {
margin-top: 16rpx;
}
.m-l-16 {
margin-left: 16rpx;
}
.color0 {
color: #000;
}
.color9 {
color: #999;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -126,4 +126,20 @@ export class PrescriptionStorage {
return null; return null;
} }
} }
/**
* 当前挂号草稿中是否已有任一分类的药品(用于复用前覆盖确认)
* @param {string|number} registerId
* @returns {boolean}
*/
static hasDraftWithDrugs(registerId) {
if (registerId === undefined || registerId === null || registerId === '') {
return false;
}
const categories = [1, 2, 3, 5, 6, 7];
return categories.some((category) => {
const data = this.loadPrescriptionData(category, registerId);
return data && Array.isArray(data.drugs) && data.drugs.length > 0;
});
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@
<view class="flex-1 m-t20"> <view class="flex-1 m-t20">
<view v-for="(it,idx) in list.west[i]" <view v-for="(it,idx) in list.west[i]"
:key="it.id"> :key="it.id">
<d-text :text="`${JSON.parse(it.content).drug_name} *${it.number}盒`" className="fs-3 main-c"></d-text> <d-text :text="formatWestDrugLine(it)" className="fs-3 main-c"></d-text>
</view> </view>
</view> </view>
</view> </view>
@@ -62,7 +62,7 @@
<view class="flex-1 m-t20"> <view class="flex-1 m-t20">
<view v-for="(it,idx) in list.chinese[i]" <view v-for="(it,idx) in list.chinese[i]"
:key="it.id"> :key="it.id">
<d-text :text="`${getName2(JSON.parse(it.content))}`" className=" fs-3 main-c"></d-text> <d-text :text="formatChineseOrGranularDrugLine(it)" className=" fs-3 main-c"></d-text>
</view> </view>
</view> </view>
</view> </view>
@@ -96,7 +96,7 @@
<view class="flex-1 m-t20"> <view class="flex-1 m-t20">
<view v-for="(it,idx) in list.granular[i]" <view v-for="(it,idx) in list.granular[i]"
:key="it.id"> :key="it.id">
<d-text :text="`${getName2(JSON.parse(it.content))}`" className=" fs-3 main-c"></d-text> <d-text :text="formatChineseOrGranularDrugLine(it)" className=" fs-3 main-c"></d-text>
</view> </view>
</view> </view>
</view> </view>
@@ -112,7 +112,7 @@
</block> </block>
</view> </view>
<view class="flex-row flex-jus-center" <view class="flex-row flex-jus-center"
v-if="!loading&&type==1&&list&&list.west_prescription&&list.west_prescription.length==0||type==2&&list&&list.chin_prescription&&list.chin_prescription.length==0"> v-if="!loading&&((type==1&&list&&list.west_prescription&&list.west_prescription.length==0)||(type==2&&list&&list.chin_prescription&&list.chin_prescription.length==0)||(type==3&&list&&list.granular_prescription&&list.granular_prescription.length==0))">
<d-empty @click="$go(1,4)"></d-empty> <d-empty @click="$go(1,4)"></d-empty>
</view> </view>
<d-loading-page :loading="loading"></d-loading-page> <d-loading-page :loading="loading"></d-loading-page>
@@ -127,22 +127,24 @@
<script> <script>
import mixin from './mixin/mixin.js'; import mixin from './mixin/mixin.js';
import { import {
commonUse, getCommonPrescriptionListApi,
deleteCommon deleteCommonPrescriptionApi
} from '@/api/all.js' } from '@/api/reception.js'
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
loading: true, loading: true,
list: '', list: {
west_prescription: [],
west: [],
chin_prescription: [],
chinese: [],
granular_prescription: [],
granular: []
},
type: 0, type: 0,
one:false, one:false
typeName:{
1:'west',
2:'chinese',
3:'granular',
}
} }
}, },
onLoad(op) { onLoad(op) {
@@ -159,33 +161,67 @@
this.type = op['type'] || 0; this.type = op['type'] || 0;
console.log(this.type,'type') console.log(this.type,'type')
this.commonUse() this.loadCommonList()
}, },
onShow() { onShow() {
this.one&&this.commonUse() this.one&&this.loadCommonList()
}, },
onHide() { onHide() {
this.one = true this.one = true
}, },
methods: { methods: {
formatWestDrugLine(it) {
if (!it) return '';
let name = it.drug_name || '';
if (it.content) {
try {
const raw = typeof it.content === 'string' ? JSON.parse(it.content) : it.content;
name = raw.drug_name || raw.name || name;
} catch (e) {}
}
return `${name} *${it.number || 1}`;
},
formatChineseOrGranularDrugLine(it) {
if (!it) return '';
if (it.content) {
try {
const parsed = typeof it.content === 'string' ? JSON.parse(it.content) : it.content;
if (Array.isArray(parsed)) {
return this.getName2(parsed);
}
if (parsed && parsed.name) {
return `${parsed.name} ${parsed.number || 1}${parsed.unit && parsed.unit.name ? parsed.unit.name : 'g'} `;
}
} catch (e) {}
}
const u = it.unit && it.unit.name ? it.unit.name : 'g';
return `${it.drug_name || it.name || ''} ${it.number || 1}${u}`;
},
handleDel (i, type, type2) { handleDel (i, type, type2) {
uni.showModal({ uni.showModal({
title: '提示信息', title: '提示信息',
content: '是否移除', content: '是否移除',
showCancel: true, showCancel: true,
success: ({ confirm, cancel }) => { success: async ({ confirm }) => {
if (confirm) { if (!confirm) return;
deleteCommon({ try {
const res = await deleteCommonPrescriptionApi({
id: this.list[type][i].id, id: this.list[type][i].id,
type: type2, type: type2
store_id: uni.getStorageSync('store_id') || 11001, });
}) if (res && res.code === 0) {
uni.showToast({ this.list[type].splice(i, 1);
title: '移除成功', const parallelKey = type2 === 'west' ? 'west' : type2 === 'chinese' ? 'chinese' : 'granular';
icon: 'success', if (this.list[parallelKey] && this.list[parallelKey][i] !== undefined) {
mask: true this.list[parallelKey].splice(i, 1);
}) }
this.list[type].splice(i, 1) uni.showToast({ title: '移除成功', icon: 'success', mask: true });
} else {
this.$toast(res.msg || res.message || '移除失败');
}
} catch (e) {
console.error(e);
this.$toast('移除失败');
} }
} }
}) })
@@ -212,25 +248,43 @@
return len return len
}, },
add() { add() {
uni.setStorageSync('add', true) uni.setStorageSync('add', true);
this.$go('index?type=' + (this.type?this.type-1:0)) let initial = 2;
if (this.type === 2) initial = 1;
else if (this.type === 3) initial = 3;
uni.navigateTo({
url: `/subPackages/sub_workbench/prescription_v2/index?save_common=1&initial_category=${initial}`
});
}, },
commonUse() { async loadCommonList() {
commonUse({ try {
type:this.typeName[this.type], const storeId = uni.getStorageSync('store_id') || 11001;
store_id: uni.getStorageSync('store_id') || 11001, const res = await getCommonPrescriptionListApi(storeId);
}).then(res => { if (res && res.code === 0 && res.result) {
if (res.errcode == 0) { this.list = res.result;
this.list = res.data
// console.log(this.list, '222')
this.$nextTick(() => {
this.loading = false
})
} else { } else {
this.$toast(res.msg) this.$toast(res.msg || res.message || '加载失败');
this.loading = false this.list = this.emptyListShape();
} }
}) } catch (e) {
console.error(e);
this.$toast('加载失败');
this.list = this.emptyListShape();
} finally {
this.$nextTick(() => {
this.loading = false;
});
}
},
emptyListShape() {
return {
west_prescription: [],
west: [],
chin_prescription: [],
chinese: [],
granular_prescription: [],
granular: []
};
} }
} }
} }

View File

@@ -199,10 +199,6 @@
<d-text text="" className="main-c fs-4" :bold="true"></d-text> <d-text text="" className="main-c fs-4" :bold="true"></d-text>
</view> </view>
<view class="flex-row" style="height: 50rpx;"> <view class="flex-row" style="height: 50rpx;">
<u-button v-if="!is_common" :throttle-time="0" shape="circle" size="mini"
:custom-style="{backgroundColor: 'transparent !important',border:'1px solid #6ACDBB',color:'#6ACDBB'}"
plain @click="$go('common?type='+(parseInt(tabsActive)+1))" :ripple="true">常用方
</u-button>
<view class="m-l-16"> <view class="m-l-16">
<u-button :throttle-time="0" :custom-style="{ backgroundColor:'#6ACDBB',color:'#fff' }" <u-button :throttle-time="0" :custom-style="{ backgroundColor:'#6ACDBB',color:'#fff' }"
shape="circle" size="mini" shape="circle" size="mini"
@@ -273,10 +269,10 @@
<!-- 按钮 --> <!-- 按钮 -->
<view class="bottom flex-jus-sp flex-ali-center"> <view class="bottom flex-jus-sp flex-ali-center">
<view v-if="!is_common" @click="add(1)" class="btnText">另存常用方</view> <view v-if="!is_common && tabsActive !== 2 && tabsActive !== 3" @click="add(1)" class="btnText">另存常用方</view>
<!-- <d-text text="请确认患者已在实体医院就诊,并有明确诊断" className="tips-c fs-24"></d-text> --> <!-- <d-text text="请确认患者已在实体医院就诊,并有明确诊断" className="tips-c fs-24"></d-text> -->
<u-button :throttle-time="0" @click="add(0)" shape="circle" <u-button :throttle-time="0" @click="add(0)" shape="circle"
:custom-style="{backgroundColor:buttonLodging?'#A5DCD2':'#6ACDBB',color:'#fff',heigth:'86rpx', width: is_common ? '686rpx' : '448rpx'}" :custom-style="{backgroundColor:buttonLodging?'#A5DCD2':'#6ACDBB',color:'#fff',heigth:'86rpx', width: (is_common || tabsActive === 2 || tabsActive === 3) ? '686rpx' : '448rpx'}"
:disabled="buttonLodging">{{is_common?'保存常用方':'发送处方'}} :disabled="buttonLodging">{{is_common?'保存常用方':'发送处方'}}
</u-button> </u-button>
</view> </view>