Files
xk-doctor-wx/subPackages/sub_business_shared/user-patient/detail.vue

752 lines
25 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>
<view>
<business-page-layout :title="pageTitle" :show-back="true">
<view class="page">
<view v-if="loading" class="empty">加载中...</view>
<template v-else>
<!-- 顶部用户 + 就诊人分区卡片固定不进 swiper -->
<view class="profile-card">
<view class="profile-row">
<image class="avatar" :src="profile.user.avatarurl || '/static/image/default-avatar.png'" mode="aspectFill" />
<view class="meta">
<text class="name">{{ profile.user.nickname || '微信用户' }}</text>
<text class="sub">ID {{ profile.user.id || '—' }}<template v-if="profile.user.mobile"> · {{ profile.user.mobile }}</template></text>
</view>
</view>
<view class="patient-block">
<view class="patient-head">
<view class="patient-head-left">
<text class="p-name">{{ profile.patient.name || '—' }}</text>
<text v-if="sexText !== '—'" class="sex-tag" :class="sexTagClass">{{ sexText }}</text>
</view>
<view class="patient-actions">
<!-- 回访拨打就诊人/用户手机 -->
<view class="callback-btn" @click.stop="onCallbackCall">
<text class="callback-btn-text">回访</text>
</view>
<!-- 记录本次回访link 按钮 -->
<view class="callback-link" @click.stop="openCallLogForm">
<text class="callback-link-text">记录本次回访</text>
</view>
</view>
</view>
<text class="p-sub">
{{ profile.patient.age || '—' }}
<template v-if="profile.patient.mobile"> · {{ profile.patient.mobile }}</template>
</text>
<!-- 门店后端不返回身份证时不展示 -->
<text v-if="profile.patient.id_card" class="p-sub">身份证 {{ profile.patient.id_card }}</text>
</view>
</view>
<!-- Tab下划线选中态 -->
<view class="tabs">
<view
v-for="(t, idx) in tabs"
:key="t.key"
class="tab"
:class="{ on: tabIndex === idx }"
@click="selectTab(idx)"
>
<text class="tab-text">{{ t.label }}</text>
<view v-if="tabIndex === idx" class="tab-bar" />
</view>
</view>
<!-- 五页左右滑资料 / 回访 / 挂号 / 处方 / 订单 -->
<swiper
class="tab-swiper"
:style="{ height: swiperHeightPx + 'px' }"
:current="tabIndex"
:duration="200"
@change="onTabSwiperChange"
>
<swiper-item v-for="t in tabs" :key="t.key">
<scroll-view
scroll-y
class="tab-scroll"
:style="{ height: swiperHeightPx + 'px' }"
>
<!-- 资料 -->
<view v-if="t.key === 'info'" class="health-card">
<text class="section-title">健康信息</text>
<template v-if="health">
<view class="info-row">
<text class="info-label">既往史</text>
<text class="info-value">{{ historyText(health.person_status, health.person_history) }}</text>
</view>
<view class="info-row">
<text class="info-label">过敏史</text>
<text
class="info-value"
:class="{ danger: Number(health.allergic_status) !== 0 }"
>{{ historyText(health.allergic_status, health.allergic_history) }}</text>
</view>
<view class="info-row">
<text class="info-label">家族遗传史</text>
<text class="info-value">{{ historyText(health.family_status, health.family_history) }}</text>
</view>
<view class="info-row">
<text class="info-label">肝功能异常</text>
<text
class="info-value"
:class="{ danger: Number(health.liver_function) !== 0 }"
>{{ functionText(health.liver_function, health.liver_index) }}</text>
</view>
<view class="info-row">
<text class="info-label">肾功能异常</text>
<text
class="info-value"
:class="{ danger: Number(health.renal_function) !== 0 }"
>{{ functionText(health.renal_function, health.renal_index) }}</text>
</view>
</template>
<view v-else class="empty-inline">暂无健康问诊记录</view>
</view>
<!-- 回访历史回访记录列表懒加载 -->
<view v-else-if="t.key === 'calllog'" class="call-log-card in-tab">
<view class="call-log-head">
<text class="call-log-head-title">回访记录</text>
<text v-if="callLogList.length" class="call-log-head-count">{{ callLogTotal }} </text>
</view>
<view v-if="callLogLoading" class="empty-inline">加载中...</view>
<view v-else-if="!callLogList.length" class="empty-inline">暂无回访记录</view>
<view v-else>
<view v-for="log in callLogList" :key="log.id" class="call-log-item">
<view class="call-log-item-head">
<text
class="call-log-result-tag"
:style="{ color: log.result_color || '#6b7280', background: log.result_bg, borderColor: log.result_color || '#e5e7eb' }"
>{{ log.result_text }}</text>
<text class="call-log-item-time">{{ log.call_at }}</text>
</view>
<view v-if="log.tags.length" class="call-log-tags">
<text
v-for="tg in log.tags"
:key="tg.value"
class="call-log-tag-text"
:style="{ color: tg.color || '#6b7280' }"
>#{{ tg.name }}</text>
</view>
<text v-if="log.conclusion" class="call-log-line">结论{{ log.conclusion }}</text>
<text v-if="log.remark" class="call-log-line">备注{{ log.remark }}</text>
<text v-if="log.next_at" class="call-log-line call-log-next">下次回访{{ log.next_at_text }}</text>
</view>
</view>
</view>
<!-- 挂号 / 处方 / 订单列表 -->
<template v-else>
<view v-if="tabLoading && activeTab === t.key" class="empty">加载中...</view>
<template v-else>
<view v-if="!listForTab(t.key).length" class="empty">暂无记录</view>
<view
v-for="item in listForTab(t.key)"
:key="item.id"
class="record-card"
@click="onRecordClick(item, t.key)"
>
<view class="record-main">
<template v-if="t.key === 'register'">
<text class="r-title">{{ item.order_no || item.order_number || '—' }}</text>
<text class="r-sub">{{ item.store || '—' }} · {{ item.doctor || '—' }}</text>
<text class="r-sub">{{ item.status_text || '—' }} · {{ item.created_at || '' }}</text>
</template>
<template v-else-if="t.key === 'prescription'">
<text class="r-title">{{ item.prescription_no || '—' }}</text>
<text class="r-sub">{{ item.store || '—' }} · {{ item.doctor || '—' }}</text>
<text class="r-sub">{{ item.clinical_diagnose || '—' }} · {{ item.created_at || '' }}</text>
</template>
<template v-else>
<text class="r-title">{{ item.order_no || '—' }}</text>
<text class="r-sub">{{ item.store || '—' }} · ¥{{ item.total_pay_price || '—' }}</text>
<text class="r-sub">{{ item.created_at || '' }}</text>
</template>
</view>
<text class="arrow"></text>
</view>
</template>
</template>
</scroll-view>
</swiper-item>
</swiper>
</template>
</view>
</business-page-layout>
<!-- page-container 抽屉挂在页面根级 -->
<prescription-detail-popup ref="prescriptionPopup" />
<!-- 回访记录表单抽屉 -->
<CallLogDrawer
v-if="callLogDrawerVisible"
:visible="callLogDrawerVisible"
:up-id="upId"
:prefill="callLogPrefill"
@update:visible="onCallLogDrawerVisibleChange"
@close="onCallLogDrawerClose"
@success="onCallLogSuccess"
/>
</view>
</template>
<script>
/**
* 就诊人详情:资料(健康问诊)+ 回访 + 挂号/处方/订单
* 顶部资料卡固定,下方五 Tab 支持点击与左右滑动切换(懒加载列表)
*/
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue';
import PrescriptionDetailPopup from '@/subPackages/sub_business_shared/order/components/PrescriptionDetailPopup.vue';
import CallLogDrawer from '@/subPackages/sub_business_shared/components/CallLogDrawer.vue';
import {
getUserPatientDetail,
getUserPatientRegisterList,
getUserPatientPrescriptionList,
getUserPatientOrderList,
getPatientCallLogList,
} from '@/api/userPatientProfile.js';
/** 就诊人详情左右滑提示只展示一次 */
const SWIPE_TIP_STORAGE_KEY = 'user_patient_detail_swipe_tip';
/** 拨号返回事件名(与 App.vue 内的 emit 配合) */
const CALLBACK_RETURNED_EVENT = 'patient:callbackReturned';
/** pendingCallback 存储键:记录拨号上下文 */
const PENDING_CALLBACK_KEY = 'pendingCallback';
/** 回访标签 code -> 中文 */
const TAG_LABEL_MAP = {
care: '关怀',
condition: '病情',
medication: '用药提醒',
review: '复诊提醒',
complaint: '投诉处理',
};
export default {
components: { BusinessPageLayout, PrescriptionDetailPopup, CallLogDrawer },
data() {
return {
upId: 0,
patientName: '',
loading: false,
tabLoading: false,
activeTab: 'info',
tabIndex: 0,
tabSwiperChanging: false,
swiperHeightPx: 400,
tabs: [
{ key: 'info', label: '资料' },
{ key: 'calllog', label: '回访' },
{ key: 'register', label: '挂号' },
{ key: 'prescription', label: '处方' },
{ key: 'order', label: '订单' },
],
profile: { user: {}, patient: {} },
health: null,
registerList: [],
prescriptionList: [],
orderList: [],
// 回访记录相关状态
callLogList: [],
callLogTotal: 0,
callLogLoading: false,
callLogDrawerVisible: false,
callLogPrefill: {},
};
},
computed: {
pageTitle() {
return this.patientName ? `就诊人 · ${this.patientName}` : '就诊人详情';
},
sexText() {
const sex = this.profile.patient && this.profile.patient.sex;
if (Number(sex) === 1) return '男';
if (Number(sex) === 2) return '女';
return '—';
},
sexTagClass() {
const sex = this.profile.patient && this.profile.patient.sex;
if (Number(sex) === 1) return 'male';
if (Number(sex) === 2) return 'female';
return '';
},
/**
* 回访拨号:只用就诊人 call_mobile拦截器只解密不脱敏
* 不用脱敏后的 mobile也不回退微信用户号
*/
callbackPhone() {
const patient = this.profile.patient || {};
return String(patient.call_mobile || '').trim();
},
},
onLoad(options) {
this.calcSwiperHeight();
this.upId = Number((options && options.up_id) || 0);
try {
this.patientName = decodeURIComponent((options && options.name) || '');
} catch (e) {
this.patientName = (options && options.name) || '';
}
// 监听 App.onShow 广播的"拨号返回"事件
uni.$on(CALLBACK_RETURNED_EVENT, this.handleCallbackReturned);
if (this.upId) {
this.loadDetail();
// 回访列表改为切到「回访」Tab 时懒加载
}
},
onReady() {
this.calcSwiperHeight();
},
/** 离开页时关掉处方抽屉,避免 page-container 残留遮罩 */
onHide() {
this.closePrescriptionPopup();
},
onUnload() {
this.closePrescriptionPopup();
uni.$off(CALLBACK_RETURNED_EVENT, this.handleCallbackReturned);
},
methods: {
/**
* 按窗口高度算死 swiper窗口 状态栏 导航 资料卡预估 Tab 边距
*/
calcSwiperHeight() {
try {
const sys = uni.getSystemInfoSync() || {};
const winH = Number(sys.windowHeight) || 600;
const statusBar = Number(sys.statusBarHeight) || 0;
const navbar = Number(this.$navbarHeight) || 44;
const rpx = (Number(sys.windowWidth) || 375) / 750;
const pagePad = Math.ceil(24 * rpx);
const profileH = Math.ceil(280 * rpx);
const tabsH = Math.ceil(90 * rpx);
const h = winH - statusBar - navbar - pagePad - profileH - tabsH;
this.swiperHeightPx = h > 180 ? h : 180;
} catch (e) {
this.swiperHeightPx = 400;
}
},
/** 按 Tab key 取对应列表swiper 多页同时渲染时不能只用 activeTab */
listForTab(key) {
if (key === 'register') return this.registerList;
if (key === 'prescription') return this.prescriptionList;
if (key === 'order') return this.orderList;
return [];
},
/** 关闭处方抽屉,防止 page-container 残留导致返回后白屏 */
closePrescriptionPopup() {
const pop = this.$refs.prescriptionPopup;
if (pop && typeof pop.onClose === 'function') {
pop.onClose();
}
},
/**
* 回访:调起系统拨号盘拨打 callbackPhone
* 无号时 toast不隐藏按钮以免布局跳动
* 同时记录 pendingCallbackApp.onShow 检测到回到小程序时
* 通过 uni.$emit 触发本页自动打开回访表单抽屉
*/
onCallbackCall() {
const phone = this.callbackPhone;
if (!phone) {
uni.showToast({ title: '暂无手机号', icon: 'none' });
return;
}
try {
uni.setStorageSync(PENDING_CALLBACK_KEY, {
upId: Number(this.upId),
phone,
ts: Date.now(),
});
} catch (e) { /* ignore */ }
uni.makePhoneCall({
phoneNumber: phone,
});
},
/** App.onShow 广播"拨号返回"事件 */
handleCallbackReturned(payload) {
if (!payload || Number(payload.upId) !== Number(this.upId)) return;
this.callLogPrefill = {
phone: payload.phone || '',
call_at: Math.floor((payload.ts || Date.now()) / 1000),
};
this.callLogDrawerVisible = true;
},
/** 手动点击"记录本次回访" */
openCallLogForm() {
this.callLogPrefill = {
phone: this.callbackPhone || '',
call_at: Math.floor(Date.now() / 1000),
};
this.callLogDrawerVisible = true;
},
/** CallLogDrawer visible 同步 */
onCallLogDrawerVisibleChange(val) {
this.callLogDrawerVisible = !!val;
},
/** 抽屉关闭 */
onCallLogDrawerClose() {
this.callLogDrawerVisible = false;
},
/** 提交成功:刷新列表 */
onCallLogSuccess() {
this.loadCallLogList();
const idx = this.tabs.findIndex((t) => t.key === 'calllog');
if (idx >= 0) {
this.selectTab(idx);
}
},
/** 拉取回访记录列表 */
async loadCallLogList() {
if (!this.upId) return;
this.callLogLoading = true;
try {
const wrap = await getPatientCallLogList(this.upId);
const body = wrap && wrap.res ? wrap.res : null;
const list = (body && body.result && Array.isArray(body.result.items)) ? body.result.items : [];
// 预算每条记录的结果标签浅色背景,避免模板 :style 调函数(小程序禁止)
this.callLogList = list.map((log) => Object.assign({}, log, {
result_bg: this.hexToRgba(log.result_color || '#6b7280', 0.12),
}));
this.callLogTotal = (body && body.result && Number(body.result.total)) || list.length;
} catch (e) {
this.callLogList = [];
this.callLogTotal = 0;
} finally {
this.callLogLoading = false;
}
},
/** hex -> rgba用于结果标签浅色背景 */
hexToRgba(hex, alpha) {
const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex || '');
if (!m) return `rgba(106,205,187,${alpha})`;
const r = parseInt(m[1], 16);
const g = parseInt(m[2], 16);
const b = parseInt(m[3], 16);
return `rgba(${r},${g},${b},${alpha})`;
},
/** tag code -> 中文 */
tagLabelOf(code) {
return TAG_LABEL_MAP[code] || code;
},
/** 既往/过敏/家族0 无,否则展示 history */
historyText(status, history) {
if (Number(status) === 0) return '无';
const t = String(history || '').trim();
return t || '有';
},
/** 肝/肾功能0 正常,异常时附带指标文案 */
functionText(flag, indexText) {
if (Number(flag) === 0) return '正常';
const t = String(indexText || '').trim();
return t ? `异常 · ${t}` : '异常';
},
async loadDetail() {
this.loading = true;
try {
const wrap = await getUserPatientDetail(this.upId);
if (wrap && wrap.ok && wrap.data) {
this.profile = {
user: wrap.data.user || {},
patient: wrap.data.patient || {},
};
this.health = wrap.data.health_inquiry || null;
if (!this.patientName && this.profile.patient.name) {
this.patientName = this.profile.patient.name;
}
}
this.calcSwiperHeight();
this.$nextTick(() => {
this.maybeShowSwipeTip();
});
} finally {
this.loading = false;
}
},
/**
* 首次进入短暂提示可左右滑动(独立缓存键,与挂号详情互不影响)
*/
maybeShowSwipeTip() {
let shown = '';
try {
shown = uni.getStorageSync(SWIPE_TIP_STORAGE_KEY);
} catch (e) {
shown = '';
}
if (shown) return;
uni.showToast({
title: '左右滑动可切换标签',
icon: 'none',
duration: 2000,
});
try {
uni.setStorageSync(SWIPE_TIP_STORAGE_KEY, '1');
} catch (e) {
// ignore
}
},
/** 点击顶部 Tab */
selectTab(index) {
if (this.tabSwiperChanging) return;
const tab = this.tabs[index];
if (!tab) return;
this.tabIndex = index;
this.switchTab(tab.key);
},
/** swiper 滑动切换 */
onTabSwiperChange(e) {
const index = e && e.detail ? e.detail.current : 0;
const tab = this.tabs[index];
if (!tab || tab.key === this.activeTab) {
this.tabIndex = index;
return;
}
this.tabSwiperChanging = true;
this.tabIndex = index;
this.switchTab(tab.key);
this.$nextTick(() => {
this.tabSwiperChanging = false;
});
},
/** 切换 Tab 并懒加载对应列表 */
async switchTab(key) {
this.activeTab = key;
const idx = this.tabs.findIndex((t) => t.key === key);
if (idx >= 0 && this.tabIndex !== idx) {
this.tabIndex = idx;
}
if (key === 'info') return;
if (key === 'calllog' && !this.callLogList.length) {
await this.loadCallLogList();
} else if (key === 'register' && !this.registerList.length) {
await this.loadRegisters();
} else if (key === 'prescription' && !this.prescriptionList.length) {
await this.loadPrescriptions();
} else if (key === 'order' && !this.orderList.length) {
await this.loadOrders();
}
},
async loadRegisters() {
this.tabLoading = true;
try {
const wrap = await getUserPatientRegisterList(this.upId);
this.registerList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
} finally {
this.tabLoading = false;
}
},
async loadPrescriptions() {
this.tabLoading = true;
try {
const wrap = await getUserPatientPrescriptionList(this.upId);
this.prescriptionList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
} finally {
this.tabLoading = false;
}
},
async loadOrders() {
this.tabLoading = true;
try {
const wrap = await getUserPatientOrderList(this.upId);
this.orderList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
} finally {
this.tabLoading = false;
}
},
/**
* 列表项点击:处方弹窗 / 订单页 / 挂号详情页
* @param item 列表行
* @param tabKey 所在 Tab避免滑动瞬间 activeTab 未同步)
*/
onRecordClick(item, tabKey) {
const key = tabKey || this.activeTab;
if (key === 'prescription') {
const pid = Number((item && item.id) || 0);
if (!pid) {
uni.showToast({ title: '缺少处方信息', icon: 'none' });
return;
}
this.$refs.prescriptionPopup && this.$refs.prescriptionPopup.open(pid);
return;
}
if (key === 'order') {
const oid = Number((item && item.id) || 0);
if (!oid) {
uni.showToast({ title: '缺少订单信息', icon: 'none' });
return;
}
// 诊所端进本包订单详情,平台端进 shared避免跨包 + page-container 残留导致列表白屏
const mode = uni.getStorageSync('loginMode') || '';
const detailUrl = mode === 'clinic_admin'
? `/subPackages/sub_clinic_admin/order/detail?id=${oid}`
: `/subPackages/sub_business_shared/order/detail?id=${oid}`;
uni.navigateTo({ url: detailUrl });
return;
}
if (key === 'register') {
// 轻量详情页用列表字段即可,暂存避免 URL 过长
try {
uni.setStorageSync('user_patient_register_detail', item || {});
} catch (e) { /* ignore */ }
uni.navigateTo({
url: `/subPackages/sub_business_shared/user-patient/register-detail?id=${Number((item && item.id) || 0)}`,
});
}
},
},
};
</script>
<style lang="scss" scoped>
.page { padding: 24rpx 24rpx 0; background: #f5f7f8; box-sizing: border-box; }
.profile-card {
background: #fff; border-radius: 20rpx; padding: 28rpx 24rpx; margin-bottom: 20rpx;
}
.profile-row { display: flex; align-items: center; gap: 20rpx; margin-bottom: 20rpx; }
.avatar { width: 72rpx; height: 72rpx; border-radius: 50%; background: #e8f5f2; }
.meta { display: flex; flex-direction: column; min-width: 0; }
.name { font-size: 28rpx; font-weight: 600; color: #1f2937; }
.sub { font-size: 22rpx; color: #9ca3af; margin-top: 6rpx; }
.patient-block { padding-top: 20rpx; border-top: 1rpx solid #eef2f1; }
.patient-head {
display: flex; align-items: center; justify-content: space-between; gap: 16rpx;
}
.patient-head-left { display: flex; align-items: center; gap: 12rpx; min-width: 0; flex: 1; }
.p-name { font-size: 30rpx; font-weight: 600; color: #111827; }
.sex-tag {
font-size: 20rpx; padding: 2rpx 12rpx; border-radius: 8rpx;
background: #e8f5f2; color: #2a9d8f;
}
.callback-btn {
flex-shrink: 0;
padding: 8rpx 22rpx;
border-radius: 24rpx;
border: 1rpx solid #6acdbb;
background: #e8f5f2;
}
.callback-btn-text { font-size: 24rpx; color: #1a9b88; font-weight: 500; }
.sex-tag.male { background: #e8f0fe; color: #3b82f6; }
.sex-tag.female { background: #fce7f3; color: #db2777; }
.p-sub { display: block; font-size: 24rpx; color: #6b7280; margin-top: 8rpx; }
.tabs {
display: flex; background: #fff; border-radius: 16rpx 16rpx 0 0;
margin-bottom: 0; padding: 0 8rpx; border-bottom: 1rpx solid #eef2f1;
}
.tab {
flex: 1; display: flex; flex-direction: column; align-items: center;
padding: 22rpx 0 16rpx; position: relative;
}
.tab-text { font-size: 26rpx; color: #6b7280; }
.tab.on .tab-text { color: #1a9b88; font-weight: 600; }
.tab-bar {
position: absolute; bottom: 0; width: 48rpx; height: 4rpx;
border-radius: 4rpx; background: #6acdbb;
}
.tab-swiper { width: 100%; }
.tab-scroll { width: 100%; box-sizing: border-box; }
.health-card {
background: #fff; border-radius: 0 0 16rpx 16rpx; padding: 24rpx;
}
.section-title {
display: block; font-size: 26rpx; font-weight: 600; color: #1f2937; margin-bottom: 16rpx;
}
.info-row {
display: flex; justify-content: space-between; align-items: flex-start;
padding: 16rpx 0; border-bottom: 1rpx solid #f3f4f6; gap: 24rpx;
}
.info-row:last-child { border-bottom: none; }
.info-label { font-size: 24rpx; color: #9ca3af; flex-shrink: 0; }
.info-value { font-size: 24rpx; color: #374151; text-align: right; flex: 1; word-break: break-all; }
.info-value.danger { color: #dc2626; font-weight: 600; }
.empty-inline { text-align: center; color: #9ca3af; padding: 40rpx 0; font-size: 24rpx; }
.record-card {
background: #fff; border-radius: 14rpx; padding: 22rpx 20rpx; margin-bottom: 12rpx;
display: flex; align-items: center; gap: 12rpx;
}
.record-main { flex: 1; min-width: 0; }
.r-title { display: block; font-size: 26rpx; font-weight: 600; color: #111827; }
.r-sub { display: block; font-size: 22rpx; color: #6b7280; margin-top: 6rpx; }
.arrow { font-size: 36rpx; color: #d1d5db; line-height: 1; padding-left: 4rpx; }
.empty { text-align: center; color: #9ca3af; padding: 80rpx 0; font-size: 28rpx; }
/* 回访/记录按钮区 */
.patient-actions {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
}
.callback-link {
padding: 8rpx 12rpx;
}
.callback-link-text {
font-size: 22rpx;
color: #1a9b88;
text-decoration: underline;
}
/* 回访记录卡片Tab 内用 in-tab 去掉外边距,与 health-card 对齐) */
.call-log-card {
background: #fff;
border-radius: 16rpx;
padding: 20rpx 24rpx;
margin-bottom: 20rpx;
}
.call-log-card.in-tab {
margin: 0;
margin-bottom: 0;
border-radius: 0;
box-shadow: none;
}
.call-log-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8rpx;
}
.call-log-head-title {
font-size: 26rpx;
font-weight: 600;
color: #1f2937;
}
.call-log-head-count {
font-size: 22rpx;
color: #9ca3af;
}
.call-log-item {
padding: 16rpx 0;
border-bottom: 1rpx solid #f3f4f6;
}
.call-log-item:last-child { border-bottom: none; }
.call-log-item-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8rpx;
}
.call-log-result-tag {
font-size: 20rpx;
padding: 2rpx 12rpx;
border-radius: 8rpx;
background: #e5e7eb;
color: #6b7280;
}
.call-log-result-tag.connected { background: #e8f5f2; color: #1a9b88; }
.call-log-result-tag.no_answer { background: #fef3c7; color: #b45309; }
.call-log-result-tag.hangup { background: #fee2e2; color: #b91c1c; }
.call-log-result-tag.invalid { background: #f3f4f6; color: #6b7280; }
.call-log-item-time {
font-size: 22rpx;
color: #9ca3af;
}
.call-log-tags {
display: flex;
flex-wrap: wrap;
gap: 8rpx;
margin: 4rpx 0;
}
.call-log-tag-text {
font-size: 20rpx;
color: #6acdbb;
}
.call-log-line {
display: block;
font-size: 24rpx;
color: #374151;
margin-top: 4rpx;
word-break: break-all;
}
.call-log-next { color: #1a9b88; }
</style>