2026-07-29 17:30:30 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<full-page pageClass="record-page" :customScroll="true">
|
2026-08-03 10:03:56 +08:00
|
|
|
|
<scroll-view
|
|
|
|
|
|
class="record-scroll"
|
|
|
|
|
|
scroll-y="true"
|
|
|
|
|
|
refresher-enabled="true"
|
|
|
|
|
|
:refresher-triggered="refreshing"
|
|
|
|
|
|
@refresherrefresh="refreshRecords"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="page-body">
|
2026-07-29 17:30:30 +08:00
|
|
|
|
<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">></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-status">{{ statusText(item) }}</view>
|
|
|
|
|
|
<view class="record-row">
|
|
|
|
|
|
<text class="row-label">【预约类型】</text>
|
|
|
|
|
|
<text class="row-value strong">{{ appointmentTypeText(item) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="record-row">
|
|
|
|
|
|
<text class="row-label">【{{ practitionerLabel(item) }}】</text>
|
|
|
|
|
|
<text class="row-value strong">{{ practitionerText(item) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="record-row">
|
|
|
|
|
|
<text class="row-label">【预约日期】</text>
|
|
|
|
|
|
<text class="row-value">{{ appointmentDateText(item) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="record-row">
|
|
|
|
|
|
<text class="row-label">【预约时间】</text>
|
|
|
|
|
|
<text class="row-value">{{ item.timeRange || "-" }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="record-row">
|
|
|
|
|
|
<text class="row-label">【预约项目】</text>
|
|
|
|
|
|
<text class="row-value">{{ projectText(item) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-08-03 10:03:56 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
2026-07-29 17:30:30 +08:00
|
|
|
|
</full-page>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { computed, ref } from "vue";
|
|
|
|
|
|
import { onLoad, onShow } from "@dcloudio/uni-app";
|
2026-08-14 14:16:15 +08:00
|
|
|
|
import { storeToRefs } from "pinia";
|
|
|
|
|
|
import useAccount from "@/store/account";
|
2026-07-29 17:30:30 +08:00
|
|
|
|
import api from "@/utils/api";
|
|
|
|
|
|
import { normalizeCorpId } from "@/utils/api-base-config";
|
2026-08-17 18:34:36 +08:00
|
|
|
|
import { useTeamAccess } from "@/hooks/use-team-access";
|
2026-08-14 14:16:15 +08:00
|
|
|
|
import { hideLoading, loading as showLoading, toast } from "@/utils/widget";
|
2026-07-29 17:30:30 +08:00
|
|
|
|
import FullPage from "@/components/full-page.vue";
|
|
|
|
|
|
|
2026-08-14 14:16:15 +08:00
|
|
|
|
const env = __VITE_ENV__;
|
|
|
|
|
|
const appid = env.MP_WX_APP_ID;
|
|
|
|
|
|
const { account, externalUserId } = storeToRefs(useAccount());
|
|
|
|
|
|
const { getTeams, getExternalUserId, login } = useAccount();
|
2026-08-17 18:34:36 +08:00
|
|
|
|
const { openTeamLogin: openTeamInviteLogin, ensureTeamAdded: ensureJoinedTeam } = useTeamAccess();
|
2026-08-14 14:16:15 +08:00
|
|
|
|
|
2026-07-29 17:30:30 +08:00
|
|
|
|
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);
|
2026-08-03 10:03:56 +08:00
|
|
|
|
const refreshing = ref(false);
|
2026-08-14 14:16:15 +08:00
|
|
|
|
const corpUserId = ref("");
|
|
|
|
|
|
const routeExternalUserId = ref("");
|
|
|
|
|
|
const authPending = ref(false);
|
|
|
|
|
|
const initialized = ref(false);
|
2026-07-29 17:30:30 +08:00
|
|
|
|
|
|
|
|
|
|
const statusMap = {
|
|
|
|
|
|
pending: { label: "未到院" },
|
|
|
|
|
|
confirmed: { label: "已到院" },
|
|
|
|
|
|
no_show: { label: "爽约" },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const avatarText = computed(() => (customerName.value || "档案").slice(0, 1));
|
|
|
|
|
|
const monthText = computed(() => selectedMonth.value.replace("-", "年") + "月");
|
|
|
|
|
|
|
2026-08-14 14:16:15 +08:00
|
|
|
|
function buildCurrentUrl() {
|
|
|
|
|
|
const params = [
|
|
|
|
|
|
`corpId=${encodeURIComponent(corpId.value || "")}`,
|
|
|
|
|
|
`teamId=${encodeURIComponent(teamId.value || "")}`,
|
|
|
|
|
|
customerId.value ? `customerId=${encodeURIComponent(customerId.value)}` : "",
|
|
|
|
|
|
customerName.value ? `name=${encodeURIComponent(customerName.value)}` : "",
|
|
|
|
|
|
corpName.value ? `corpName=${encodeURIComponent(corpName.value)}` : "",
|
|
|
|
|
|
corpUserId.value ? `corpUserId=${encodeURIComponent(corpUserId.value)}` : "",
|
|
|
|
|
|
routeExternalUserId.value ? `externalUserId=${encodeURIComponent(routeExternalUserId.value)}` : "",
|
|
|
|
|
|
selectedMonth.value ? `month=${encodeURIComponent(selectedMonth.value)}` : "",
|
|
|
|
|
|
].filter(Boolean).join("&");
|
|
|
|
|
|
return `/pages/record/appointment-record?${params}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensureLogin() {
|
|
|
|
|
|
if (!account.value) await login();
|
|
|
|
|
|
if (!account.value) {
|
2026-08-17 18:34:36 +08:00
|
|
|
|
await ensureCustomerContext();
|
|
|
|
|
|
await openTeamLogin();
|
2026-08-14 14:16:15 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensurePhoneAuthorized() {
|
|
|
|
|
|
if (account.value?.mobile) return true;
|
|
|
|
|
|
await ensureCustomerContext();
|
2026-08-17 18:34:36 +08:00
|
|
|
|
return openTeamLogin();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function openTeamLogin() {
|
2026-08-14 14:16:15 +08:00
|
|
|
|
if (!corpId.value || !teamId.value) {
|
|
|
|
|
|
toast("预约团队信息缺失,暂无法查看预约记录");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-08-17 18:34:36 +08:00
|
|
|
|
const res = await openTeamInviteLogin({
|
2026-08-14 14:16:15 +08:00
|
|
|
|
corpId: corpId.value,
|
|
|
|
|
|
teamId: teamId.value,
|
|
|
|
|
|
corpUserId: corpUserId.value || "",
|
|
|
|
|
|
externalUserId: routeExternalUserId.value || externalUserId.value || "",
|
2026-08-17 18:34:36 +08:00
|
|
|
|
fallbackCorpName: corpName.value || "",
|
|
|
|
|
|
redirectUrl: buildCurrentUrl(),
|
2026-08-14 14:16:15 +08:00
|
|
|
|
});
|
2026-08-17 18:34:36 +08:00
|
|
|
|
if (!res.success) toast(res.message);
|
2026-08-14 14:16:15 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensureCustomerContext() {
|
|
|
|
|
|
if (!corpId.value || !customerId.value) return null;
|
|
|
|
|
|
const res = await api("getCustomerByCustomerId", { corpId: corpId.value, customerId: customerId.value }, false);
|
|
|
|
|
|
const customer = res?.success && res.data ? res.data : null;
|
|
|
|
|
|
if (!customer) return null;
|
|
|
|
|
|
if (!customerName.value && customer.name) customerName.value = customer.name;
|
|
|
|
|
|
const teamIds = Array.isArray(customer.teamId) ? customer.teamId : [customer.teamId].filter(Boolean);
|
|
|
|
|
|
if (!teamId.value) teamId.value = teamIds.find(Boolean) || "";
|
|
|
|
|
|
return customer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensureTeamAdded() {
|
|
|
|
|
|
await ensureCustomerContext();
|
|
|
|
|
|
if (!corpId.value || !teamId.value) {
|
|
|
|
|
|
toast("预约团队信息缺失,暂无法查看预约记录");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
showLoading("添加团队中...");
|
|
|
|
|
|
try {
|
2026-08-17 18:34:36 +08:00
|
|
|
|
const res = await ensureJoinedTeam({
|
2026-08-14 14:16:15 +08:00
|
|
|
|
appid,
|
2026-08-17 18:34:36 +08:00
|
|
|
|
account: account.value,
|
|
|
|
|
|
getTeams,
|
2026-08-14 14:16:15 +08:00
|
|
|
|
corpId: corpId.value,
|
|
|
|
|
|
teamId: teamId.value,
|
2026-08-17 18:34:36 +08:00
|
|
|
|
});
|
|
|
|
|
|
if (!res.success) {
|
|
|
|
|
|
toast(res.message);
|
2026-08-14 14:16:15 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensureArchiveBound() {
|
|
|
|
|
|
const miniAppId = account.value?.openid || "";
|
|
|
|
|
|
if (!miniAppId || !corpId.value || !customerId.value) return false;
|
|
|
|
|
|
const customer = await ensureCustomerContext();
|
|
|
|
|
|
if (customer && String(customer.miniAppId || "") === String(miniAppId)) return true;
|
|
|
|
|
|
authPending.value = true;
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/archive/edit-archive?corpId=${encodeURIComponent(corpId.value)}&teamId=${encodeURIComponent(teamId.value)}&bindCustomerId=${encodeURIComponent(customerId.value)}&source=appointmentRegistration`,
|
|
|
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensureAccessReady() {
|
2026-08-17 18:34:36 +08:00
|
|
|
|
await ensureCustomerContext();
|
2026-08-14 14:16:15 +08:00
|
|
|
|
const okLogin = await ensureLogin();
|
|
|
|
|
|
if (!okLogin) return false;
|
|
|
|
|
|
const okPhone = await ensurePhoneAuthorized();
|
|
|
|
|
|
if (!okPhone) return false;
|
|
|
|
|
|
await ensureCustomerContext();
|
|
|
|
|
|
if (corpId.value) await getExternalUserId(corpId.value);
|
|
|
|
|
|
const okTeam = await ensureTeamAdded();
|
|
|
|
|
|
if (!okTeam) return false;
|
|
|
|
|
|
const okArchive = await ensureArchiveBound();
|
|
|
|
|
|
if (!okArchive) return false;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-12 18:09:41 +08:00
|
|
|
|
async function loadCorpName() {
|
|
|
|
|
|
if (!corpId.value) {
|
|
|
|
|
|
corpName.value = "";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const res = await api("getCorpInfo", { corpId: corpId.value }, false);
|
|
|
|
|
|
const corp = Array.isArray(res?.data) ? res.data[0] : null;
|
|
|
|
|
|
corpName.value = corp?.corp_name || corp?.corpName || "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-29 17:30:30 +08:00
|
|
|
|
function pad(value) {
|
|
|
|
|
|
return String(value).padStart(2, "0");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function toDate(ts) {
|
|
|
|
|
|
if (!ts) return null;
|
|
|
|
|
|
const d = new Date(Number(ts));
|
|
|
|
|
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatMonth(ts) {
|
|
|
|
|
|
const d = toDate(ts) || new Date();
|
|
|
|
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatDate(ts) {
|
|
|
|
|
|
const d = toDate(ts);
|
|
|
|
|
|
if (!d) return "";
|
|
|
|
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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: `${start.getFullYear()}-${pad(start.getMonth() + 1)}-01`,
|
|
|
|
|
|
end: `${end.getFullYear()}-${pad(end.getMonth() + 1)}-${pad(end.getDate())}`,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function appointmentDateText(item) {
|
|
|
|
|
|
return item?.appointmentDateStr || formatDate(item?.appointmentDate || item?.startTime || item?.registrationTime) || "-";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function statusText(item) {
|
|
|
|
|
|
return statusMap[item?.status || "pending"]?.label || "未到院";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function appointmentTypeText(item) {
|
2026-08-14 14:16:15 +08:00
|
|
|
|
if (item?.appointmentType === "followup") return "复诊预约";
|
|
|
|
|
|
if (item?.appointmentType === "treatment") return "治疗预约";
|
|
|
|
|
|
return "其他预约";
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function practitionerLabel(item) {
|
2026-08-14 14:16:15 +08:00
|
|
|
|
return item?.appointmentType === "treatment" ? "治疗师" : "医生";
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function practitionerText(item) {
|
|
|
|
|
|
const id = item?.therapistUserId || "";
|
|
|
|
|
|
if (item?.therapistName) return item.therapistName;
|
|
|
|
|
|
if (id === "none-therapist" || id === "none-doctor") return "未指定人员";
|
|
|
|
|
|
return staffNameMap.value[id] || id || "未指定人员";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function projectText(item) {
|
|
|
|
|
|
const projects = item?.treatmentProject;
|
|
|
|
|
|
if (Array.isArray(projects)) {
|
|
|
|
|
|
const names = projects
|
|
|
|
|
|
.map((project) => {
|
|
|
|
|
|
if (!project) return "";
|
|
|
|
|
|
if (typeof project === "string") return project;
|
|
|
|
|
|
return project.projectName || project.name || project.title || "";
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
return names.length ? names.join("、") : "无";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeof projects === "string" && projects) return projects;
|
|
|
|
|
|
return "无";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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=appointment&corpId=${corp}&teamId=${team}&customerId=${customer}`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-03 10:03:56 +08:00
|
|
|
|
async function loadRecords(options = {}) {
|
|
|
|
|
|
const { silent = false } = options;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
if (!corpId.value || !customerId.value) {
|
|
|
|
|
|
records.value = [];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-08-03 10:03:56 +08:00
|
|
|
|
if (!silent) loading.value = true;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const range = monthRange(selectedMonth.value);
|
|
|
|
|
|
const res = await api(
|
|
|
|
|
|
"getAppointmentRegistration",
|
|
|
|
|
|
{
|
|
|
|
|
|
corpId: corpId.value,
|
|
|
|
|
|
customerId: customerId.value,
|
|
|
|
|
|
appointmentDateStart: range.start,
|
|
|
|
|
|
appointmentDateEnd: range.end,
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
pageSize: 200,
|
|
|
|
|
|
sortBy: "appointmentDate",
|
|
|
|
|
|
sortOrder: "desc",
|
|
|
|
|
|
},
|
|
|
|
|
|
false
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
|
toast(res?.message || "加载预约记录失败");
|
|
|
|
|
|
records.value = [];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
records.value = (Array.isArray(res.data) ? res.data : []).sort((a, b) => {
|
|
|
|
|
|
const aTime = Number(a.appointmentDate || a.startTime || a.registrationTime || 0);
|
|
|
|
|
|
const bTime = Number(b.appointmentDate || b.startTime || b.registrationTime || 0);
|
|
|
|
|
|
return bTime - aTime;
|
|
|
|
|
|
});
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-03 10:03:56 +08:00
|
|
|
|
async function refreshRecords() {
|
|
|
|
|
|
if (refreshing.value) return;
|
|
|
|
|
|
refreshing.value = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await loadStaffNameMap();
|
|
|
|
|
|
await loadRecords({ silent: true });
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
refreshing.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-29 17:30:30 +08:00
|
|
|
|
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) : "";
|
2026-08-14 14:16:15 +08:00
|
|
|
|
corpName.value = options.corpName ? decodeURIComponent(options.corpName) : "";
|
|
|
|
|
|
corpUserId.value = options.corpUserId || "";
|
|
|
|
|
|
routeExternalUserId.value = options.externalUserId || "";
|
2026-07-29 17:30:30 +08:00
|
|
|
|
if (options.month) selectedMonth.value = options.month;
|
2026-08-12 18:09:41 +08:00
|
|
|
|
await loadCorpName();
|
2026-08-14 14:16:15 +08:00
|
|
|
|
const ready = await ensureAccessReady();
|
|
|
|
|
|
if (!ready) return;
|
|
|
|
|
|
initialized.value = true;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
await loadStaffNameMap();
|
|
|
|
|
|
await loadRecords();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-08-14 14:16:15 +08:00
|
|
|
|
onShow(async () => {
|
|
|
|
|
|
if (authPending.value) {
|
|
|
|
|
|
authPending.value = false;
|
|
|
|
|
|
const ready = await ensureAccessReady();
|
|
|
|
|
|
if (!ready) return;
|
|
|
|
|
|
initialized.value = true;
|
|
|
|
|
|
await loadStaffNameMap();
|
|
|
|
|
|
await loadRecords();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (initialized.value && corpId.value && customerId.value) loadRecords();
|
2026-07-29 17:30:30 +08:00
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2026-08-03 10:03:56 +08:00
|
|
|
|
.record-page :deep(.page-scroll) {
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.record-scroll {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
background: #e9edf3;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-29 17:30:30 +08:00
|
|
|
|
.page-body {
|
|
|
|
|
|
min-height: 100%;
|
|
|
|
|
|
box-sizing: border-box;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
background: #e9edf3;
|
|
|
|
|
|
border-top: 8rpx solid #0b63d8;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.profile-card {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 18rpx;
|
|
|
|
|
|
padding: 20rpx 24rpx;
|
|
|
|
|
|
background: #fff;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
border-bottom: 1rpx solid #d7dce5;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.avatar-wrap {
|
|
|
|
|
|
width: 72rpx;
|
|
|
|
|
|
height: 72rpx;
|
|
|
|
|
|
border-radius: 50%;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
background: #d2d5da;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
overflow: hidden;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
flex-shrink: 0;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.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 {
|
2026-07-30 16:37:11 +08:00
|
|
|
|
font-size: 31rpx;
|
|
|
|
|
|
font-weight: 700;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
color: #333;
|
|
|
|
|
|
line-height: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.profile-sub {
|
|
|
|
|
|
margin-top: 4rpx;
|
|
|
|
|
|
font-size: 24rpx;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
color: #9aa0a8;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
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;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
border-bottom: 1rpx solid #d4d9e2;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.month-arrow {
|
|
|
|
|
|
font-size: 18rpx;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
}
|
|
|
|
|
|
.record-list {
|
2026-07-30 16:37:11 +08:00
|
|
|
|
padding: 0rpx 10rpx 12rpx 10rpx;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.record-card {
|
|
|
|
|
|
position: relative;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
padding: 22rpx 24rpx 22rpx 34rpx;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
background: #fff;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
border-top: 14rpx solid #e9edf3;
|
|
|
|
|
|
border-bottom: 1rpx solid #d7dce5;
|
|
|
|
|
|
}
|
|
|
|
|
|
.record-card::before {
|
|
|
|
|
|
display: none;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.record-status {
|
|
|
|
|
|
position: absolute;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
top: 24rpx;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
right: 24rpx;
|
|
|
|
|
|
color: #333;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
background: transparent;
|
|
|
|
|
|
padding: 0;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.record-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
gap: 0;
|
|
|
|
|
|
padding: 7rpx 112rpx 7rpx 0;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
font-size: 27rpx;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
line-height: 38rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.record-row:first-of-type {
|
|
|
|
|
|
padding-top: 0;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.row-label {
|
|
|
|
|
|
flex-shrink: 0;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
color: #6b7280;
|
|
|
|
|
|
font-weight: 700;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.row-value {
|
|
|
|
|
|
min-width: 0;
|
2026-07-30 16:37:11 +08:00
|
|
|
|
text-align: left;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
color: #333;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
.strong {
|
2026-07-30 16:37:11 +08:00
|
|
|
|
color: #333;
|
|
|
|
|
|
font-weight: 700;
|
2026-07-29 17:30:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
.empty {
|
|
|
|
|
|
padding: 120rpx 0;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
}
|
2026-08-17 18:34:36 +08:00
|
|
|
|
</style>
|