页面完善

This commit is contained in:
zhanchao 2026-07-30 16:37:11 +08:00
parent cc069d0af1
commit 6314ceb65a
4 changed files with 250 additions and 109 deletions

View File

@ -100,7 +100,8 @@ function buildTargetUrl(item) {
return `/pages/health/list?teamId=${team}&corpId=${corp}&id=${customer}&name=${name}`;
}
if (target.value === "treatment") {
return `/pages/record/treatment-record?teamId=${team}&corpId=${corp}&customerId=${customer}&name=${name}`;
const corpName = encodeURIComponent(item.corpName || "");
return `/pages/record/treatment-record?teamId=${team}&corpId=${corp}&customerId=${customer}&name=${name}&corpName=${corpName}`;
}
if (target.value === "appointment") {
const corpName = encodeURIComponent(item.corpName || "");

View File

@ -236,7 +236,8 @@ onShow(() => {
.page-body {
min-height: 100%;
box-sizing: border-box;
background: #eef1f6;
background: #e9edf3;
border-top: 8rpx solid #0b63d8;
}
.profile-card {
display: flex;
@ -244,14 +245,15 @@ onShow(() => {
gap: 18rpx;
padding: 20rpx 24rpx;
background: #fff;
border-bottom: 1rpx solid #d9dde5;
border-bottom: 1rpx solid #d7dce5;
}
.avatar-wrap {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: #d8d8d8;
background: #d2d5da;
overflow: hidden;
flex-shrink: 0;
}
.avatar-placeholder {
width: 100%;
@ -268,15 +270,15 @@ onShow(() => {
min-width: 0;
}
.profile-title {
font-size: 30rpx;
font-weight: 600;
font-size: 31rpx;
font-weight: 700;
color: #333;
line-height: 40rpx;
}
.profile-sub {
margin-top: 4rpx;
font-size: 24rpx;
color: #999;
color: #9aa0a8;
line-height: 34rpx;
}
.profile-arrow {
@ -293,49 +295,61 @@ onShow(() => {
background: #e2e6ec;
color: #333;
font-size: 30rpx;
font-weight: 600;
border-bottom: 1rpx solid #d5dae3;
font-weight: 700;
border-bottom: 1rpx solid #d4d9e2;
}
.month-arrow {
font-size: 18rpx;
color: #333;
}
.record-list {
padding-bottom: 24rpx;
padding: 0rpx 10rpx 12rpx 10rpx;
}
.record-card {
position: relative;
padding: 20rpx 24rpx 20rpx 34rpx;
padding: 22rpx 24rpx 22rpx 34rpx;
background: #fff;
border-bottom: 1rpx solid #d9dde5;
border-top: 14rpx solid #eef1f6;
border-top: 14rpx solid #e9edf3;
border-bottom: 1rpx solid #d7dce5;
}
.record-card::before {
display: none;
}
.record-status {
position: absolute;
top: 22rpx;
top: 24rpx;
right: 24rpx;
color: #333;
background: transparent;
padding: 0;
font-size: 26rpx;
}
.record-row {
display: flex;
align-items: flex-start;
padding: 7rpx 110rpx 7rpx 0;
justify-content: flex-start;
gap: 0;
padding: 7rpx 112rpx 7rpx 0;
font-size: 27rpx;
line-height: 36rpx;
line-height: 38rpx;
}
.record-row:first-of-type {
padding-top: 0;
}
.row-label {
flex-shrink: 0;
color: #666;
font-weight: 600;
color: #6b7280;
font-weight: 700;
}
.row-value {
min-width: 0;
text-align: left;
color: #333;
word-break: break-all;
}
.strong {
font-weight: 600;
color: #333;
font-weight: 700;
}
.empty {
padding: 120rpx 0;

View File

@ -1,41 +1,67 @@
<template>
<full-page pageClass="record-page" :customScroll="true">
<view class="page-body">
<view class="profile-card">
<view class="profile-title">{{ customerName || "未选择档案" }}</view>
<view class="profile-sub">治疗记录</view>
<view class="profile-card" @click="switchArchive">
<view class="avatar-wrap">
<view class="avatar-placeholder">{{ avatarText }}</view>
</view>
<view class="profile-main">
<view class="profile-title">{{ customerName || "请选择档案" }}</view>
<view class="profile-sub">所属机构{{ corpName || "-" }}</view>
</view>
<view class="profile-arrow">&gt;</view>
</view>
<picker mode="date" fields="month" :value="selectedMonth" @change="handleMonthChange">
<view class="month-bar">
<text>{{ monthText }}</text>
<text class="month-arrow"></text>
</view>
</picker>
<view v-if="loading" class="empty">加载中...</view>
<view v-else-if="!records.length" class="empty">暂无治疗记录</view>
<view v-else class="record-list">
<view v-for="item in records" :key="item._id" class="record-card">
<view class="record-head">
<view class="record-title">{{ item.projectName || "未命名项目" }}</view>
<view class="status-tag" :class="statusClass(item)">{{ statusText(item) }}</view>
<view class="record-status">已治疗</view>
<view class="record-row">
<text class="row-label">项目名称</text>
<text class="row-value strong">{{ item.projectName || "未命名项目" }}</text>
</view>
<view class="record-row">
<text class="row-label">治疗科室</text>
<text class="row-label">治疗日期</text>
<text class="row-value">{{ formatDate(item.treatmentTime) }}</text>
</view>
<view class="record-row">
<text class="row-label">治疗时间</text>
<text class="row-value">{{ formatTimeOnly(item.treatmentTime) }}</text>
</view>
<view class="record-row">
<text class="row-label">治疗医生</text>
<text class="row-value strong">{{ item.treatmentDoctorName || item.doctorName || staffText(item.treatmentDoctorUserId) }}</text>
</view>
<view v-if="assistantText(item)" class="record-row">
<text class="row-label">配台</text>
<text class="row-value">{{ assistantText(item) }}</text>
</view>
<view class="record-row">
<text class="row-label">治疗科室</text>
<text class="row-value">{{ item.treatmentDeptName || "-" }}</text>
</view>
<view class="record-row">
<text class="row-label">购买数量</text>
<text class="row-value">{{ item.usageCount || 0 }}</text>
<text class="row-label">治疗数</text>
<text class="row-value">{{ item.deductUsageCount || 0 }}</text>
</view>
<view class="record-row">
<text class="row-label">剩余数量</text>
<text class="row-value strong">{{ item.restUsageCount || 0 }}</text>
<view v-if="item.treatmentArea" class="record-row">
<text class="row-label">治疗部位</text>
<text class="row-value">{{ item.treatmentArea }}</text>
</view>
<view class="record-row">
<text class="row-label">有效期</text>
<text class="row-value">{{ formatValidTime(item.validTime) }}</text>
</view>
<view class="record-row">
<text class="row-label">购买日期</text>
<text class="row-value">{{ formatTime(item.createTreatementTime || item.billTime) }}</text>
<view v-if="item.treatmentRemark" class="record-row">
<text class="row-label">治疗备注</text>
<text class="row-value">{{ item.treatmentRemark }}</text>
</view>
<view v-if="item.packageName" class="record-row">
<text class="row-label">所属套餐</text>
<text class="row-label">所属套餐</text>
<text class="row-value">{{ item.packageName }}</text>
</view>
</view>
@ -45,7 +71,7 @@
</template>
<script setup>
import { ref } from "vue";
import { computed, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import api from "@/utils/api";
import { normalizeCorpId } from "@/utils/api-base-config";
@ -53,41 +79,88 @@ import { toast } from "@/utils/widget";
import FullPage from "@/components/full-page.vue";
const corpId = ref("");
const teamId = ref("");
const customerId = ref("");
const customerName = ref("");
const corpName = ref("");
const selectedMonth = ref(formatMonth(Date.now()));
const records = ref([]);
const staffNameMap = ref({});
const loading = ref(false);
const statusMap = {
init: { label: "待治疗", className: "status-wait" },
pending: { label: "治疗中", className: "status-doing" },
treated: { label: "已治疗", className: "status-done" },
void: { label: "已作废", className: "status-cancel" },
};
const avatarText = computed(() => (customerName.value || "档案").slice(0, 1));
const monthText = computed(() => selectedMonth.value.replace("-", "年") + "月");
function formatTime(ts) {
if (!ts) return "-";
function pad(value) {
return String(value).padStart(2, "0");
}
function toDate(ts) {
if (!ts) return null;
const d = new Date(Number(ts));
if (Number.isNaN(d.getTime())) return "-";
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
const h = String(d.getHours()).padStart(2, "0");
const min = String(d.getMinutes()).padStart(2, "0");
return `${y}-${m}-${day} ${h}:${min}`;
return Number.isNaN(d.getTime()) ? null : d;
}
function formatValidTime(ts) {
if (!ts) return "无限期";
return formatTime(ts).slice(0, 10);
function formatMonth(ts) {
const d = toDate(ts) || new Date();
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}`;
}
function statusText(item) {
return statusMap[item?.treatmentStatus]?.label || item?.treatmentStatusStr || "-";
function monthRange(month) {
const [year, monthIndex] = String(month || "").split("-").map(Number);
const start = new Date(year, monthIndex - 1, 1);
const end = new Date(year, monthIndex, 0);
return [
`${start.getFullYear()}-${pad(start.getMonth() + 1)}-01`,
`${end.getFullYear()}-${pad(end.getMonth() + 1)}-${pad(end.getDate())}`,
];
}
function statusClass(item) {
return statusMap[item?.treatmentStatus]?.className || "status-wait";
function formatDate(ts) {
const d = toDate(ts);
if (!d) return "-";
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}
function formatTimeOnly(ts) {
const d = toDate(ts);
if (!d) return "-";
return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
function staffText(userid) {
if (!userid) return "-";
return staffNameMap.value[userid] || userid;
}
function assistantText(item) {
const assistants = item?.assistantDoctors;
if (!Array.isArray(assistants) || !assistants.length) return "";
return assistants.map((staff) => staffText(staff?.userid || staff?.userId || staff)).filter(Boolean).join("、");
}
async function loadStaffNameMap() {
if (!corpId.value) {
staffNameMap.value = {};
return;
}
const res = await api("getAllCorpMemberIncludeDeleted", { corpId: corpId.value }, false);
const list = res?.success && Array.isArray(res.data) ? res.data : [];
staffNameMap.value = list.reduce((map, staff) => {
if (staff?.userid) {
map[staff.userid] = staff.anotherName || staff.name || staff.userid;
}
return map;
}, {});
}
function switchArchive() {
const corp = encodeURIComponent(corpId.value || "");
const team = encodeURIComponent(teamId.value || "");
const customer = encodeURIComponent(customerId.value || "");
uni.navigateTo({
url: `/pages/experience-coupon/select-rights-archive?mode=target&target=treatment&corpId=${corp}&teamId=${team}&customerId=${customer}`,
});
}
async function loadRecords() {
@ -98,10 +171,12 @@ async function loadRecords() {
loading.value = true;
try {
const res = await api(
"getTreatmentRecord",
"getDeductRecord",
{
corpId: corpId.value,
customerId: customerId.value,
deductStatus: ["deducted"],
treatmentTimeDates: monthRange(selectedMonth.value),
page: 1,
pageSize: 200,
},
@ -112,17 +187,29 @@ async function loadRecords() {
records.value = [];
return;
}
records.value = res.list || res.data?.list || [];
const list = res.data?.list || res.list || [];
records.value = Array.isArray(list)
? list.sort((a, b) => Number(b.treatmentTime || b.createTime || 0) - Number(a.treatmentTime || a.createTime || 0))
: [];
} finally {
loading.value = false;
}
}
async function handleMonthChange(event) {
selectedMonth.value = event?.detail?.value || selectedMonth.value;
await loadRecords();
}
onLoad(async (options = {}) => {
uni.setNavigationBarTitle({ title: "治疗记录" });
corpId.value = normalizeCorpId(options.corpId || "");
teamId.value = options.teamId || "";
customerId.value = options.customerId || options.id || "";
customerName.value = options.name ? decodeURIComponent(options.name) : "";
corpName.value = options.corpName ? decodeURIComponent(options.corpName) : "";
if (options.month) selectedMonth.value = options.month;
await loadStaffNameMap();
await loadRecords();
});
@ -134,71 +221,109 @@ onShow(() => {
<style scoped>
.page-body {
min-height: 100%;
padding: 24rpx;
box-sizing: border-box;
background: #f5f6fa;
background: #e9edf3;
border-top: 8rpx solid #0b63d8;
}
.profile-card,
.record-card {
.profile-card {
display: flex;
align-items: center;
gap: 18rpx;
padding: 20rpx 24rpx;
background: #fff;
border-radius: 18rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
border-bottom: 1rpx solid #d7dce5;
}
.avatar-wrap {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: #d2d5da;
overflow: hidden;
flex-shrink: 0;
}
.avatar-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 30rpx;
font-weight: 600;
}
.profile-main {
flex: 1;
min-width: 0;
}
.profile-title {
font-size: 34rpx;
font-weight: 600;
color: #1f2329;
font-size: 31rpx;
font-weight: 700;
color: #333;
line-height: 40rpx;
}
.profile-sub {
margin-top: 8rpx;
margin-top: 4rpx;
font-size: 24rpx;
color: #8a8f99;
color: #9aa0a8;
line-height: 34rpx;
}
.profile-arrow {
flex-shrink: 0;
color: #b4b7bf;
font-size: 32rpx;
}
.month-bar {
display: flex;
align-items: center;
gap: 8rpx;
height: 70rpx;
padding: 0 24rpx;
background: #e2e6ec;
color: #333;
font-size: 30rpx;
font-weight: 700;
border-bottom: 1rpx solid #d4d9e2;
}
.month-arrow {
font-size: 18rpx;
color: #333;
}
.record-list {
padding: 0rpx 10rpx 12rpx 10rpx;
}
.record-card {
position: relative;
padding: 22rpx 24rpx 22rpx 34rpx;
background: #fff;
border-top: 14rpx solid #e9edf3;
border-bottom: 1rpx solid #d7dce5;
}
.record-status {
position: absolute;
top: 24rpx;
right: 24rpx;
color: #333;
font-size: 26rpx;
}
.record-head,
.record-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 20rpx;
}
.record-head {
margin-bottom: 18rpx;
}
.record-title {
flex: 1;
min-width: 0;
font-size: 32rpx;
font-weight: 600;
color: #1f2329;
}
.status-tag {
flex-shrink: 0;
padding: 6rpx 16rpx;
border-radius: 999rpx;
font-size: 22rpx;
}
.status-wait { color: #065bd6; background: rgba(6, 91, 214, 0.1); }
.status-doing { color: #d97706; background: rgba(217, 119, 6, 0.12); }
.status-done { color: #16a34a; background: rgba(22, 163, 74, 0.12); }
.status-cancel { color: #8a8f99; background: #f1f2f4; }
.record-row {
padding: 10rpx 0;
font-size: 26rpx;
padding: 7rpx 112rpx 7rpx 0;
font-size: 27rpx;
line-height: 38rpx;
}
.row-label {
flex-shrink: 0;
color: #8a8f99;
color: #6b7280;
font-weight: 700;
}
.row-value {
min-width: 0;
text-align: right;
color: #1f2329;
color: #333;
word-break: break-all;
}
.strong {
color: #065bd6;
font-weight: 600;
font-weight: 700;
}
.empty {
padding: 120rpx 0;

View File

@ -60,6 +60,7 @@ const urlsConfig = {
getMiniAppCustomers: 'getMiniAppCustomers',
getTeamCustomers: 'getTeamCustomers',
getTreatmentRecord: "getTreatmentRecord",
getDeductRecord: "getDeductRecord",
getAppointmentRecord: "getAppointmentRecord",
getAppointmentRegistration: "getAppointmentRegistration",
getUnbindMiniAppCustomers: 'getUnbindMiniAppCustomers',