fix: 常用诊断、常用医嘱

This commit is contained in:
2025-12-25 16:00:42 +08:00
parent 7f2ed4b30a
commit f4e51dc773
4 changed files with 483 additions and 38 deletions

View File

@@ -4,11 +4,15 @@ import {ref} from 'vue';
import {Page, useVbenModal} from '@vben/common-ui';
import {
Badge,
PlusOutlined,
DeleteOutlined,
} from '@ant-design/icons-vue';
import {
Button,
Col,
InputSearch,
message,
Popconfirm,
Row,
} from 'ant-design-vue';
@@ -16,7 +20,11 @@ import {
getDiseaseList,
} from '#/views/doctor/doctor-reception/api';
import { debounce } from 'lodash-es';
import {getDoctorMyDiseaseListApi} from "#/views/doctor/settings/api"; // 或者使用自定义防抖函数
import {
getDoctorMyDiseaseListApi,
addDoctorMyDiseaseApi,
deleteDoctorMyDiseaseApi,
} from "#/views/doctor/settings/api";
const searchKey = ref('');
const diagnosisList = ref([]);
@@ -78,6 +86,10 @@ function getDiagnosisList(searchKey = '') {
getDiseaseList(searchKey).then((res) => {
diagnosisList.value = res.map((item) => {
item.isSelect = 0;
// 检查是否已在常用诊断中
item.isInMyList = doctorMyDiseaseList.value.some(
(myItem) => myItem.disease.id === item.id
);
if (selectDiagnosisList.value) {
// 把values的字符串转为数组根据分解
const valuesArr = selectDiagnosisList.value.split('');
@@ -96,6 +108,7 @@ function getDiagnosisList(searchKey = '') {
const getDiagnosisListChage = debounce(async (searchText = '') => {
getDiagnosisList(searchText);
}, 300)
/**
* 选择诊断
* @param item
@@ -119,10 +132,48 @@ function selectDiagnosis(item) {
// 将数组转回字符串更新到响应式变量
selectDiagnosisList.value = arr.join('');
}
/**
* 添加到常用诊断
* @param item 诊断项
*/
async function addToMyDiagnosis(item) {
try {
await addDoctorMyDiseaseApi(item.id);
message.success(`已将「${item.name}」添加到常用诊断`);
// 刷新常用诊断列表
await getDoctorMyDiseaseList();
// 更新搜索结果中的状态
item.isInMyList = true;
} catch (error) {
message.error('添加失败,请重试');
}
}
/**
* 从常用诊断中删除
* @param item 常用诊断项包含id和disease
*/
async function removeFromMyDiagnosis(item) {
try {
await deleteDoctorMyDiseaseApi(item.id);
message.success(`已将「${item.disease.name}」从常用诊断中移除`);
// 刷新常用诊断列表
await getDoctorMyDiseaseList();
// 更新搜索结果中的状态
const searchItem = diagnosisList.value.find(
(d) => d.id === item.disease.id
);
if (searchItem) {
searchItem.isInMyList = false;
}
} catch (error) {
message.error('删除失败,请重试');
}
}
</script>
<template>
<!-- <Modal class="w-[60%]" title="西(中成)药列表">-->
<Modal class="w-[60%]" title="常用诊断">
<Page>
<Row :gutter="12">
@@ -136,19 +187,152 @@ function selectDiagnosis(item) {
/>
</Col>
<Col :span="24">
<h1 class="mt-3" v-if="doctorMyDiseaseList.length > 0">常用</h1>
<Badge v-for="item in doctorMyDiseaseList" :count="0" class="mt-5">
<Button style="margin: 0 10px;" type="primary" :ghost="item.disease.isSelect !== 1" @click="selectDiagnosis(item.disease)">{{ item.disease.name }}</Button>
</Badge>
<!-- 常用诊断列表 -->
<h1 class="diagnosis-section-title" v-if="doctorMyDiseaseList.length > 0">常用</h1>
<div class="diagnosis-list">
<div
v-for="item in doctorMyDiseaseList"
:key="item.id"
class="diagnosis-item"
:class="{ 'is-selected': item.disease.isSelect === 1 }"
>
<Button
type="primary"
:ghost="item.disease.isSelect !== 1"
@click="selectDiagnosis(item.disease)"
>
{{ item.disease.name }}
</Button>
<div class="diagnosis-actions" @click.stop>
<Popconfirm
title="确定从常用诊断中移除吗?"
@confirm="removeFromMyDiagnosis(item)"
>
<Button
type="text"
size="small"
danger
class="action-btn"
>
<template #icon><DeleteOutlined /></template>
</Button>
</Popconfirm>
</div>
</div>
</div>
<h1 class="mt-3" v-if="diagnosisList.length > 0">搜索结果</h1>
<Badge v-for="item in diagnosisList" :count="0" class="mt-5">
<Button style="margin: 0 10px;" type="primary" :ghost="item.isSelect !== 1" @click="selectDiagnosis(item)">{{ item.name }}</Button>
</Badge>
<!-- 搜索结果列表 -->
<h1 class="diagnosis-section-title" v-if="diagnosisList.length > 0">搜索结果</h1>
<div class="diagnosis-list">
<div
v-for="item in diagnosisList"
:key="item.id"
class="diagnosis-item"
:class="{ 'is-selected': item.isSelect === 1 }"
>
<Button
type="primary"
:ghost="item.isSelect !== 1"
@click="selectDiagnosis(item)"
>
{{ item.name }}
</Button>
<div class="diagnosis-actions" @click.stop>
<Button
v-if="!item.isInMyList"
type="text"
size="small"
class="action-btn add-btn"
@click="addToMyDiagnosis(item)"
>
<template #icon><PlusOutlined /></template>
<span class="action-text">加入常用</span>
</Button>
<span v-else class="already-added">已添加</span>
</div>
</div>
</div>
</Col>
</Row>
</Page>
</Modal>
</template>
<style lang="scss">
<style lang="scss" scoped>
.diagnosis-section-title {
margin-top: 12px;
margin-bottom: 8px;
font-size: 14px;
font-weight: 600;
color: #333;
}
.dark .diagnosis-section-title {
color: #e5e7eb;
}
.diagnosis-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.diagnosis-item {
position: relative;
display: inline-flex;
align-items: center;
margin: 4px 0;
&:hover .diagnosis-actions {
opacity: 1;
visibility: visible;
}
}
.diagnosis-actions {
position: absolute;
right: -8px;
top: -8px;
display: flex;
align-items: center;
gap: 2px;
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
padding: 2px;
}
.dark .diagnosis-actions {
background: #374151;
}
.action-btn {
display: flex;
align-items: center;
font-size: 12px;
padding: 2px 6px;
height: auto;
}
.add-btn {
color: #1890ff;
&:hover {
color: #40a9ff;
background: rgba(24, 144, 255, 0.1);
}
}
.action-text {
margin-left: 2px;
}
.already-added {
font-size: 11px;
color: #999;
padding: 2px 6px;
}
</style>

View File

@@ -4,23 +4,36 @@ import {ref} from 'vue';
import {Page, useVbenModal} from '@vben/common-ui';
import {
Badge,
PlusOutlined,
DeleteOutlined,
} from '@ant-design/icons-vue';
import {
Button,
Col,
InputSearch,
Input,
message,
Popconfirm,
Row,
} from 'ant-design-vue';
import {
getDiseaseList, getDoctorOrderList,
getDoctorOrderList,
} from '#/views/doctor/doctor-reception/api';
import {
createDoctorOrderApi,
deleteDoctorOrderApi,
} from '#/views/doctor/settings/api';
const searchKey = ref('');
const doctorOrderCommonList = ref([]);
const doctorOrderMyList = ref([]);
const selectDiagnosisList = ref('');
const setUpdateDoctorOrder = ref();
// 新增医嘱相关
const newOrderContent = ref('');
const showAddInput = ref(false);
const [Modal, modalApi] = useVbenModal({
fullscreenButton: false,
draggable: true,
@@ -40,16 +53,15 @@ const [Modal, modalApi] = useVbenModal({
if (values) {
selectDiagnosisList.value = values;
}
getDiagnosisList();
getDoctorOrderListData();
}
},
});
/**
* 获取诊断列表
* @param searchKey
* 获取医嘱列表
*/
function getDiagnosisList() {
function getDoctorOrderListData() {
getDoctorOrderList().then((res) => {
doctorOrderCommonList.value = res.common.map((item) => {
item.isSelect = 0;
@@ -83,10 +95,11 @@ function getDiagnosisList() {
}
/**
* 选择诊断
* 选择医嘱
* @param item
* @param type 1=我的医嘱(content字段), 2=公共医嘱(name字段)
*/
function selectDiagnosis(item, type = 1) {
function selectDoctorOrder(item, type = 1) {
// 将当前值转换为数组(过滤空值)
let arr = selectDiagnosisList.value ? selectDiagnosisList.value.split('').filter(Boolean) : [];
@@ -119,34 +132,258 @@ function selectDiagnosis(item, type = 1) {
// 将数组转回字符串更新到响应式变量
selectDiagnosisList.value = arr.join('');
}
/**
* 添加自定义医嘱
*/
async function addMyDoctorOrder() {
if (!newOrderContent.value.trim()) {
message.warning('请输入医嘱内容');
return;
}
try {
await createDoctorOrderApi(newOrderContent.value.trim());
message.success('添加成功');
newOrderContent.value = '';
showAddInput.value = false;
// 刷新列表
await getDoctorOrderListData();
} catch (error) {
message.error('添加失败,请重试');
}
}
/**
* 删除我的医嘱
* @param item 医嘱项
*/
async function deleteMyDoctorOrder(item) {
try {
await deleteDoctorOrderApi(item.id);
message.success('删除成功');
// 刷新列表
await getDoctorOrderListData();
} catch (error) {
message.error('删除失败,请重试');
}
}
/**
* 取消添加
*/
function cancelAdd() {
newOrderContent.value = '';
showAddInput.value = false;
}
</script>
<template>
<!-- <Modal class="w-[60%]" title="西(中成)药列表">-->
<Modal class="w-[60%]" title="常用医嘱">
<Page>
<Row :gutter="12">
<!-- 我的医嘱 -->
<Col :span="24">
<InputSearch
:model="searchKey"
enter-button
placeholder="请输想要搜索的内容..."
@search="getDiagnosisList"
/>
<h1 class="order-section-title">我的医嘱</h1>
<div class="order-list">
<div
v-for="item in doctorOrderMyList"
:key="item.id"
class="order-item"
:class="{ 'is-selected': item.isSelect === 1 }"
>
<Button
type="primary"
:ghost="item.isSelect !== 1"
@click="selectDoctorOrder(item, 1)"
>
{{ item.content }}
</Button>
<div class="order-actions" @click.stop>
<Popconfirm
title="确定删除这条医嘱吗?"
@confirm="deleteMyDoctorOrder(item)"
>
<Button
type="text"
size="small"
danger
class="action-btn"
>
<template #icon><DeleteOutlined /></template>
</Button>
</Popconfirm>
</div>
</div>
<!-- 添加医嘱按钮/输入框 -->
<div
v-if="!showAddInput"
class="add-order-btn"
@click="showAddInput = true"
>
<PlusOutlined />
<span>添加医嘱</span>
</div>
<div v-else class="add-order-input">
<Input
v-model:value="newOrderContent"
placeholder="输入医嘱内容"
@pressEnter="addMyDoctorOrder"
/>
<div class="add-actions">
<Button size="small" @click="cancelAdd">取消</Button>
<Button type="primary" size="small" @click="addMyDoctorOrder">
添加
</Button>
</div>
</div>
</div>
</Col>
<!-- 公共医嘱 -->
<Col :span="24">
<Badge v-for="item in doctorOrderMyList" :count="0" class="mt-5">
<Button style="margin: 0 10px;" type="primary" :ghost="item.isSelect !== 1" @click="selectDiagnosis(item)">{{ item.content }}</Button>
</Badge>
</Col>
<Col :span="24">
<Badge v-for="item in doctorOrderCommonList" :count="0" class="mt-5">
<Button style="margin: 0 10px;" type="primary" :ghost="item.isSelect !== 1" @click="selectDiagnosis(item, 2)">{{ item.name }}</Button>
</Badge>
<h1 class="order-section-title" v-if="doctorOrderCommonList.length > 0">公共医嘱</h1>
<div class="order-list">
<div
v-for="item in doctorOrderCommonList"
:key="item.id"
class="order-item"
:class="{ 'is-selected': item.isSelect === 1 }"
>
<Button
type="primary"
:ghost="item.isSelect !== 1"
@click="selectDoctorOrder(item, 2)"
>
{{ item.name }}
</Button>
</div>
</div>
</Col>
</Row>
</Page>
</Modal>
</template>
<style lang="scss">
<style lang="scss" scoped>
.order-section-title {
margin-top: 12px;
margin-bottom: 8px;
font-size: 14px;
font-weight: 600;
color: #333;
}
.dark .order-section-title {
color: #e5e7eb;
}
.order-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.order-item {
position: relative;
display: inline-flex;
align-items: center;
margin: 4px 0;
&:hover .order-actions {
opacity: 1;
visibility: visible;
}
}
.order-actions {
position: absolute;
right: -8px;
top: -8px;
display: flex;
align-items: center;
gap: 2px;
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
padding: 2px;
}
.dark .order-actions {
background: #374151;
}
.action-btn {
display: flex;
align-items: center;
font-size: 12px;
padding: 2px 6px;
height: auto;
}
.add-order-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
color: #666;
transition: all 0.3s;
&:hover {
border-color: #1890ff;
color: #1890ff;
}
}
.dark .add-order-btn {
border-color: #4b5563;
color: #9ca3af;
&:hover {
border-color: #3b82f6;
color: #3b82f6;
}
}
.add-order-input {
display: flex;
align-items: center;
gap: 8px;
padding: 4px;
border: 1px solid #1890ff;
border-radius: 6px;
background: #fff;
:deep(.ant-input) {
flex: 1;
border: none;
outline: none;
box-shadow: none;
font-size: 13px;
&:focus {
box-shadow: none;
}
}
}
.dark .add-order-input {
background: #374151;
border-color: #3b82f6;
}
.add-actions {
display: flex;
gap: 4px;
}
</style>

View File

@@ -34,3 +34,27 @@ export async function saveServicePriceApi(data: Record<string, any>) {
export async function createDoctorOrderApi(content: string) {
return requestClient.post<any>(`${prefix}create-doctor-order`, { content });
}
/**
* 删除医嘱
* @param id 医嘱ID
*/
export async function deleteDoctorOrderApi(id: number) {
return requestClient.post<any>(`${prefix}delete-doctor-order`, { id });
}
/**
* 添加常用诊断
* @param disease_id 疾病ID
*/
export async function addDoctorMyDiseaseApi(disease_id: number) {
return requestClient.post<any>(`${prefix}add-my-disease`, { disease_id });
}
/**
* 删除常用诊断
* @param id 常用诊断记录ID
*/
export async function deleteDoctorMyDiseaseApi(id: number) {
return requestClient.post<any>(`${prefix}delete-my-disease`, { id });
}

View File

@@ -24,7 +24,7 @@ export const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'id', align: 'left', title: 'ID', width: 100 },
{ field: 'desc', align: 'left', title: '协议名称' },
{ field: 'content', title: '协议介绍', slots: { default: 'content' } },
// { field: 'content', title: '协议介绍', slots: { default: 'content' } },
{ field: 'end_txt', title: '协议端口' },
{ field: 'created_at', title: '创建时间' },
{ type: 'html', title: '操作', slots: { default: 'action' } },