Files
hospital-admin/src/views/basic/pharmacy/components/pharmacyDoctorDrawer.vue
T

522 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<a-drawer
v-model:visible="visible"
:title="drawerTitle"
:width="1080"
:footer="false"
@cancel="handleClose"
>
<!-- 药房信息摘要卡片 -->
<a-card :bordered="false" class="pharmacy-summary-card">
<div class="pharmacy-info-box">
<div class="pharmacy-icon-box">
<icon-home style="font-size: 28px; color: #165dff" />
</div>
<div class="pharmacy-meta">
<div class="pharmacy-meta-title">
<span class="name">{{ currentPharmacy?.pharmacy_name || '-' }}</span>
<a-tag color="blue" size="small" style="margin-left: 8px">
{{ currentPharmacy?.pharmacy_code || '-' }}
</a-tag>
<a-tag v-if="currentPharmacy?.status === 1" color="green" size="small" style="margin-left: 4px">
运营中
</a-tag>
<a-tag v-else color="red" size="small" style="margin-left: 4px">
已停用
</a-tag>
</div>
<div class="pharmacy-meta-desc">
<span><strong>基础邮费:</strong>¥{{ Number(currentPharmacy?.postage || 0).toFixed(2) }}</span>
<span style="margin-left: 20px">
<strong>包邮门槛:</strong>
{{ Number(currentPharmacy?.free_shipping_threshold) > 0 ? `¥${Number(currentPharmacy.free_shipping_threshold).toFixed(2)}` : '不设门槛' }}
</span>
<span style="margin-left: 20px">
<strong>支持自提:</strong>{{ currentPharmacy?.is_pickup === 1 ? '是' : '否' }}
</span>
<span style="margin-left: 20px">
<strong>联系电话:</strong>{{ currentPharmacy?.telephone || '-' }}
</span>
</div>
<div class="pharmacy-meta-address">
<strong>详细地址:</strong>{{ currentPharmacy?.full_address || currentPharmacy?.address || '-' }}
</div>
</div>
</div>
</a-card>
<a-divider style="margin: 16px 0" />
<!-- 筛选表单 -->
<a-form :model="queryForm" ref="queryFormRef" layout="inline" class="search-form">
<a-form-item field="doctor_name" label="医生姓名">
<a-input
v-model="queryForm.doctor_name"
placeholder="请输入医生姓名"
allow-clear
style="width: 170px"
@press-enter="handleQuery"
/>
</a-form-item>
<a-form-item field="mobile" label="手机号码">
<a-input
v-model="queryForm.mobile"
placeholder="请输入手机号码"
allow-clear
style="width: 170px"
@press-enter="handleQuery"
/>
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" size="small" @click="handleQuery">
<template #icon><icon-search /></template>
搜索
</a-button>
<a-button size="small" @click="handleResetQuery">
<template #icon><icon-loop /></template>
重置
</a-button>
</a-space>
</a-form-item>
</a-form>
<!-- 工具栏:数据统计与导出操作 -->
<div class="table-toolbar">
<div class="table-toolbar-left">
<span class="table-total-tip">共找到 {{ pager.total }} 位关联医生</span>
</div>
<div class="table-toolbar-right">
<a-dropdown
v-if="hasPermission('admin:doctorPharmacy:export') || hasPermission('admin:sysPharmacy:doctor')"
@select="handleExportCommand"
>
<a-button type="outline" size="small" :loading="exportLoading">
<template #icon><icon-download /></template>
导出医生数据
<icon-down style="margin-left: 4px" />
</a-button>
<template #content>
<a-doption :value="1">
<template #icon><icon-filter /></template>
导出当前筛选数据
</a-doption>
<a-doption :value="2" :disabled="selectedRowKeys.length === 0">
<template #icon><icon-check-square /></template>
导出已选数据 {{ selectedRowKeys.length ? `(${selectedRowKeys.length})` : '' }}
</a-doption>
<a-doption :value="3">
<template #icon><icon-layers /></template>
导出该药房全部医生
</a-doption>
</template>
</a-dropdown>
</div>
</div>
<!-- 医生列表表格 -->
<a-table
:columns="columns"
:data="tableData"
:loading="loading"
:pagination="paginationProps"
row-key="doctor_pharmacy_id"
:row-selection="rowSelection"
v-model:selected-keys="selectedRowKeys"
:scroll="{ x: 860 }"
style="margin-top: 10px"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<!-- 序号 -->
<template #index="{ rowIndex }">
{{ (pager.page - 1) * pager.page_size + rowIndex + 1 }}
</template>
<!-- 医生信息 -->
<template #doctor="{ record }">
<div class="doctor-cell">
<a-avatar :size="36" shape="circle">
<img v-if="record.avatar" :src="record.avatar" alt="avatar" />
<icon-user v-else />
</a-avatar>
<div class="doctor-cell-info">
<div class="doctor-name">{{ record.user_name || '-' }}</div>
<a-tag size="mini" color="blue">{{ record.doctor_title_name || '医生' }}</a-tag>
</div>
</div>
</template>
<!-- 所属医院与科室 -->
<template #hospital="{ record }">
<div>{{ record.hospital_name || '-' }}</div>
<div style="font-size: 12px; color: #86909c">{{ record.department_custom_name || '-' }}</div>
</template>
<!-- 是否默认 -->
<template #is_default="{ record }">
<a-tag v-if="record.is_default === 1" color="arcoblue" size="small">
<template #icon><icon-check-circle-fill /></template>
首选默认
</a-tag>
<a-tag v-else color="gray" size="small">普通关联</a-tag>
</template>
<!-- 操作列 -->
<template #action="{ record }">
<a-space :size="4">
<a-button type="text" size="small" @click="handleViewDoctor(record)">
<template #icon><icon-eye /></template>
档案
</a-button>
<a-popconfirm
v-if="hasPermission('admin:doctorPharmacy:remove')"
content="确定解除该医生与当前药房的绑定关系?"
type="warning"
@ok="handleRemoveBinding(record)"
>
<a-button type="text" status="danger" size="small">
<template #icon><icon-delete /></template>
解绑
</a-button>
</a-popconfirm>
</a-space>
</template>
</a-table>
<!-- 公共医生详情模态弹窗 -->
<DoctorModal
:doctorVisible="doctorModalVisible"
:doctor_id="selectedDoctorId"
@doctorVisibleChange="() => { doctorModalVisible = false; selectedDoctorId = ''; }"
/>
</a-drawer>
</template>
<script setup>
import { ref, reactive, computed } from 'vue';
import { Message } from '@arco-design/web-vue';
import { usePermissionStore } from '@/store/permission';
import { getPharmacyDoctorPage, exportPharmacyDoctor } from '@/api/basic/pharmacy';
import { removeDoctorPharmacy } from '@/api/doctor/pharmacy';
import DoctorModal from '@/components/doctorModal.vue';
const permissionStore = usePermissionStore();
// 权限校验
const hasPermission = (permission) => {
const perms = permissionStore.buttonPermissions || [];
return perms.includes('*') || perms.includes(permission);
};
const visible = ref(false);
const loading = ref(false);
const exportLoading = ref(false);
const currentPharmacy = ref(null);
const tableData = ref([]);
const selectedRowKeys = ref([]);
// 表格多选配置
const rowSelection = reactive({
type: 'checkbox',
showCheckedAll: true,
onlyCurrentPage: false,
});
// 医生弹窗查看状态
const doctorModalVisible = ref(false);
const selectedDoctorId = ref('');
const queryFormRef = ref(null);
const queryForm = reactive({
doctor_name: '',
mobile: '',
});
const pager = reactive({
page: 1,
page_size: 10,
total: 0,
});
const paginationProps = computed(() => ({
total: pager.total,
current: pager.page,
pageSize: pager.page_size,
showTotal: true,
showJumper: true,
showPageSize: true,
pageSizeOptions: [10, 20, 50],
}));
const drawerTitle = computed(() => {
const name = currentPharmacy.value?.pharmacy_name || '';
return name ? `关联医生列表 - ${name}` : '关联医生列表';
});
// 表格列配置
const columns = [
{ title: '#', slotName: 'index', width: 50, align: 'center' },
{ title: '医生信息', slotName: 'doctor', width: 170 },
{ title: '手机号码', dataIndex: 'mobile', width: 130 },
{ title: '执业机构与科室', slotName: 'hospital', minWidth: 180 },
{ title: '默认状态', slotName: 'is_default', width: 110, align: 'center' },
{ title: '绑定时间', dataIndex: 'bind_time', width: 170 },
{ title: '操作', slotName: 'action', width: 180, align: 'center', fixed: 'right' },
];
// 获取药房绑定的医生列表数据
const fetchTableData = async () => {
if (!currentPharmacy.value?.pharmacy_id) return;
loading.value = true;
try {
const params = {
pharmacy_id: String(currentPharmacy.value.pharmacy_id),
page: pager.page,
page_size: pager.page_size,
};
if (queryForm.doctor_name) params.doctor_name = queryForm.doctor_name;
if (queryForm.mobile) params.mobile = queryForm.mobile;
const res = await getPharmacyDoctorPage(params);
if (res.code === 200 && res.data) {
tableData.value = res.data.data || [];
pager.total = res.data.total || 0;
pager.page = res.data.page || 1;
pager.page_size = res.data.page_size || 10;
} else {
Message.error(res.message || '获取药房关联医生列表失败');
}
} catch (error) {
console.error('获取关联医生列表异常:', error);
} finally {
loading.value = false;
}
};
// 搜索
const handleQuery = () => {
pager.page = 1;
fetchTableData();
};
// 重置搜索
const handleResetQuery = () => {
queryForm.doctor_name = '';
queryForm.mobile = '';
pager.page = 1;
selectedRowKeys.value = [];
fetchTableData();
};
// 分页变化
const handlePageChange = (page) => {
pager.page = page;
fetchTableData();
};
const handlePageSizeChange = (pageSize) => {
pager.page_size = pageSize;
pager.page = 1;
fetchTableData();
};
// 查看医生档案
const handleViewDoctor = (record) => {
selectedDoctorId.value = String(record.doctor_id);
doctorModalVisible.value = true;
};
// 解绑医生
const handleRemoveBinding = async (record) => {
try {
const res = await removeDoctorPharmacy(
record.doctor_pharmacy_id || record.pharmacy_id
);
if (res.code === 200) {
Message.success('已解除医生绑定');
if (tableData.value.length === 1 && pager.page > 1) {
pager.page -= 1;
}
fetchTableData();
} else {
Message.error(res.message || '解除绑定失败');
}
} catch (error) {
Message.error('解除绑定出现异常');
}
};
// 通用文件安全下载方法
const triggerDownload = (fileUrl, fileName = '药房关联医生列表') => {
if (!fileUrl) return;
const link = document.createElement('a');
link.href = fileUrl;
link.setAttribute('download', fileName);
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 药房关联医生列表导出
const handleExport = async (type) => {
const pharmacyId = currentPharmacy.value?.pharmacy_id;
if (!pharmacyId) {
Message.warning('未能获取当前药房信息');
return;
}
// 场景 2:按选中导出需校验勾选项
if (type === 2 && (!selectedRowKeys.value || selectedRowKeys.value.length === 0)) {
Message.warning('请先勾选需要导出的医生数据');
return;
}
const params = {
type,
pharmacy_id: String(pharmacyId),
};
if (type === 1) {
if (queryForm.doctor_name) params.doctor_name = queryForm.doctor_name.trim();
if (queryForm.mobile) params.mobile = queryForm.mobile.trim();
} else if (type === 2) {
params.id = selectedRowKeys.value.join(',');
}
exportLoading.value = true;
try {
const res = await exportPharmacyDoctor(params);
if (res.code === 200 && res.data) {
Message.success('导出成功,正在下载文件...');
const pharmacyName = currentPharmacy.value?.pharmacy_name || '药房';
triggerDownload(res.data, `${pharmacyName}_关联医生.xlsx`);
} else {
Message.error(res.message || '导出失败,请稍后重试');
}
} catch (error) {
console.error('导出异常:', error);
Message.error('导出请求异常,请联系系统管理员');
} finally {
exportLoading.value = false;
}
};
// 下拉菜单导出命令分发
const handleExportCommand = (value) => {
handleExport(Number(value));
};
// 打开抽屉
const open = (pharmacy) => {
currentPharmacy.value = pharmacy;
queryForm.doctor_name = '';
queryForm.mobile = '';
pager.page = 1;
selectedRowKeys.value = [];
visible.value = true;
fetchTableData();
};
const handleClose = () => {
selectedRowKeys.value = [];
visible.value = false;
};
defineExpose({
open,
});
</script>
<style lang="scss" scoped>
.table-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
padding: 0 2px;
.table-total-tip {
font-size: 13px;
color: #86909c;
}
}
.pharmacy-summary-card {
background-color: #f7f8fa;
border-radius: 6px;
:deep(.arco-card-body) {
padding: 12px 16px;
}
}
.pharmacy-info-box {
display: flex;
align-items: flex-start;
.pharmacy-icon-box {
width: 48px;
height: 48px;
border-radius: 8px;
background-color: #e8f3ff;
display: flex;
justify-content: center;
align-items: center;
margin-right: 14px;
flex-shrink: 0;
}
.pharmacy-meta {
flex: 1;
.pharmacy-meta-title {
display: flex;
align-items: center;
.name {
font-size: 16px;
font-weight: 600;
color: #1d2129;
}
}
.pharmacy-meta-desc {
margin-top: 6px;
font-size: 13px;
color: #4e5969;
}
.pharmacy-meta-address {
margin-top: 4px;
font-size: 13px;
color: #86909c;
}
}
}
.search-form {
:deep(.arco-form-item) {
margin-bottom: 8px;
}
}
.doctor-cell {
display: flex;
align-items: center;
.doctor-cell-info {
margin-left: 10px;
.doctor-name {
font-weight: 500;
color: #1d2129;
margin-bottom: 2px;
}
}
}
</style>