522 lines
15 KiB
Vue
522 lines
15 KiB
Vue
<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>
|