ykt-team-wxapp/pages/team/components/BusinessCard.vue

313 lines
7.7 KiB
Vue
Raw Normal View History

2026-06-23 15:36:34 +08:00
<template>
<view v-if="card" class="business-card" @click="handleClick">
<image class="card-bg" :src="cardBg"></image>
<view class="card-header">
<view class="avatar">
<image :src="card.avatar || '/static/card/avatar.png'" mode="aspectFill"></image>
</view>
<view class="info">
<text class="name">{{ card.externalName || card.anotherName || '' }}</text>
<text v-if="card.hospitalInfo" class="hospital">{{ card.hospitalInfo.name || '' }}</text>
<view class="title-tags">
<text class="title-tag department" v-if="card.deptName">{{ card.deptName }}</text>
<text class="title-tag position" v-if="card.title">{{ card.title }}</text>
</view>
</view>
</view>
<view class="contact-section">
<view class="contact-info">
<!-- <view class="contact-item" v-if="card.externalContact">
<image class="contact-icon" src="/static/card/address.png" mode="aspectFit"></image>
<text class="contact-text">{{ accountInfo.address }}</text>
</view> -->
<view v-if="card.externalContact" class="contact-item">
<image class="contact-icon" src="/static/card/phone.png" mode="aspectFit"></image>
<text class="contact-text">{{ card.externalContact || '' }}</text>
</view>
<view v-if="card.externalEmail" class="contact-item">
<image class="contact-icon" src="/static/card/email.png" mode="aspectFit"></image>
<text class="contact-text">{{ card.externalEmail || '' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed, onMounted, ref } from "vue";
import { onShow } from '@dcloudio/uni-app'
// import { useCardStore } from "@/stores/card";
// import { useAccountStore } from "@/stores/account";
// import { getDeptStaffDepartments } from "@/api/dept";
const props = defineProps({
card: {
type: [Object, null],
default: null
},
// 点击事件处理函数
onClick: {
type: Function,
default: null,
},
// 背景图片路径
cardBg: {
type: String,
default: "/static/card/card-bg2.png",
},
// 显示模式:'doctor' 医生模式(使用 cardStore 的可见性设置),'patient' 患者模式(直接显示)
mode: {
type: String,
default: "doctor",
validator: (value) => ["doctor", "patient"].includes(value),
},
// 自定义账户信息(如果提供,则使用自定义数据而不是 store
customAccountInfo: {
type: Object,
default: null,
},
// 自定义机构信息(如果提供,则使用自定义数据而不是 store
customInstitutionInfo: {
type: Object,
default: null,
},
// 自定义医生名片状态(如果提供,则使用自定义数据而不是 store
customDoctorCardStatus: {
type: Object,
default: null,
},
});
const emit = defineEmits(["click"]);
const cardStore = ref({});
const accountStore = ref({});
// 获取账户信息(优先使用自定义数据)
const accountInfo = computed(() => {
if (props.customAccountInfo) {
return props.customAccountInfo;
}
return accountStore.getAccountInfo || {};
});
// 部门显示名称(优先使用接口 getDeptStaffDepartments 返回的结果)
const deptDisplayName = ref("");
const displayDeptName = computed(() => {
// 优先展示接口返回的部门字符串,否则回退账户里的 deptName
return deptDisplayName.value || accountInfo.value.deptName || "";
});
// 获取机构信息(优先使用自定义数据)
const institutionInfo = computed(() => {
if (props.customInstitutionInfo) {
return props.customInstitutionInfo;
}
return accountStore.getInstitutionInfo || {};
});
// 获取医生名片状态(优先使用自定义数据)
const doctorCardStatus = computed(() => {
if (props.customDoctorCardStatus) {
return props.customDoctorCardStatus;
}
return accountStore.getDoctorCardStatus || { hasDoctorCard: false };
});
// 根据模式决定是否显示部门名称
const shouldShowDeptName = computed(() => {
if (props.mode === "patient") {
return true; // 患者模式直接显示
}
return cardStore.deptNameVisible; // 医生模式使用 cardStore 设置
});
// 根据模式决定是否显示职位
const shouldShowPosition = computed(() => {
if (props.mode === "patient") {
return true; // 患者模式直接显示
}
return cardStore.positionVisible; // 医生模式使用 cardStore 设置
});
// 根据模式决定是否显示电话
const shouldShowPhone = computed(() => {
if (props.mode === "patient") {
return true; // 患者模式直接显示
}
return cardStore.phoneVisible; // 医生模式使用 cardStore 设置
});
// 根据模式决定是否显示邮箱
const shouldShowEmail = computed(() => {
if (props.mode === "patient") {
return true; // 患者模式直接显示
}
return cardStore.emailVisible; // 医生模式使用 cardStore 设置
});
// 根据模式决定是否显示地址
const shouldShowAddress = computed(() => {
if (props.mode === "patient") {
return true; // 患者模式直接显示
}
return cardStore.addressVisible; // 医生模式使用 cardStore 设置
});
// 处理点击事件
const handleClick = () => {
if (props.onClick) {
props.onClick();
}
emit("click");
};
// 组件挂载后,通过工号 staffId 调用 getDeptStaffDepartments 获取部门显示信息
onShow(async () => {
try {
const staffId = accountInfo.value.staffId;
if (!staffId) return;
const res = await getDeptStaffDepartments({ staffId });
if (res && res.success && Array.isArray(res.data) && res.data.length > 0) {
// 后端返回的是部门名称数组,例如 ["人力资源部/人事培训科/人事培训科"]
deptDisplayName.value = res.data.join("、");
}
} catch (error) {
console.error("获取部门显示信息失败:", error);
}
});
</script>
<style scoped lang="scss">
/* 名片展示区域 */
.business-card {
position: relative;
overflow: visible;
border-bottom-left-radius: 16rpx;
border-bottom-right-radius: 16rpx;
font-size: 0;
}
.card-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
display: block;
}
.card-header {
position: relative;
padding: 36rpx 40rpx 10rpx 40rpx;
display: flex;
align-items: center;
margin-bottom: 36rpx;
}
.contact-section {
position: relative;
z-index: 1;
padding: 0 40rpx 56rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.avatar {
width: 120rpx;
height: 120rpx;
display: flex;
border-radius: 50%;
align-items: center;
justify-content: center;
font-size: 60rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
flex-shrink: 0;
margin-right: 30rpx;
align-self: center;
image {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.name {
display: block;
font-size: 36rpx;
font-weight: 500;
margin-bottom: 6rpx;
color: #222;
}
.hospital {
display: block;
font-size: 26rpx;
margin-bottom: 8rpx;
color: #666;
}
.title-tags {
display: flex;
gap: 16rpx;
margin-top: 8rpx;
}
.title-tag {
font-size: 24rpx;
font-weight: 400;
padding: 0 16rpx;
flex-shrink: 0;
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: center;
}
.title-tag.department {
color: #ff8b00;
border: 1rpx solid #ff8b00;
}
.title-tag.position {
color: #de4f27;
border: 0.5px solid #de4f27;
}
.contact-info {
display: flex;
flex-direction: column;
gap: 10rpx;
align-items: flex-start;
flex: 1;
}
.contact-item {
display: flex;
align-items: center;
gap: 10rpx;
}
.contact-icon {
width: 32rpx;
height: 32rpx;
}
.contact-text {
font-size: 24rpx;
color: #666;
}
</style>