2026-02-02 11:20:15 +08:00
|
|
|
<template>
|
|
|
|
|
<view class="page-container">
|
|
|
|
|
<view class="header-bg"></view>
|
2026-02-09 15:17:45 +08:00
|
|
|
|
2026-02-02 11:20:15 +08:00
|
|
|
<view class="user-card">
|
|
|
|
|
<view class="avatar-container">
|
|
|
|
|
<uni-icons type="person-filled" size="60" color="#cccccc" class="default-avatar"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="user-info">
|
|
|
|
|
<text class="nickname">微信用户</text>
|
|
|
|
|
<text class="phone">{{ maskedPhone }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
2026-07-29 11:25:29 +08:00
|
|
|
<view class="shortcut-bar">
|
|
|
|
|
<view class="shortcut-item" @click="openRightsWallet">
|
|
|
|
|
<view class="shortcut-icon shortcut-icon--rights">
|
|
|
|
|
<uni-icons type="gift" size="28" color="#065bd6"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="shortcut-title">我的权益</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="shortcut-item" @click="openCouponWallet">
|
|
|
|
|
<view class="shortcut-icon shortcut-icon--coupon">
|
|
|
|
|
<uni-icons type="wallet" size="28" color="#ff8a00"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="shortcut-title">我的体验券</text>
|
|
|
|
|
</view>
|
2026-07-29 17:30:30 +08:00
|
|
|
<view class="shortcut-item" @click="openTreatmentRecord">
|
2026-07-29 11:25:29 +08:00
|
|
|
<view class="shortcut-icon shortcut-icon--record">
|
|
|
|
|
<uni-icons type="compose" size="28" color="#19be6b"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="shortcut-title">治疗记录</text>
|
|
|
|
|
</view>
|
2026-07-29 17:30:30 +08:00
|
|
|
<view class="shortcut-item" @click="openAppointmentRecord">
|
|
|
|
|
<view class="shortcut-icon shortcut-icon--appointment">
|
|
|
|
|
<uni-icons type="calendar" size="28" color="#7c3aed"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="shortcut-title">预约记录</text>
|
|
|
|
|
</view>
|
2026-07-29 11:25:29 +08:00
|
|
|
</view>
|
|
|
|
|
|
2026-02-02 11:20:15 +08:00
|
|
|
<view class="menu-container">
|
2026-02-09 15:17:45 +08:00
|
|
|
<view class="px-15 py-12 flex items-center border-b" @click="toPage('/pages/mine/contact')">
|
|
|
|
|
<view class="flex-shrink-0 item-icon">
|
|
|
|
|
<uni-icons type="headphones" size="22" color="#000"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="mr-10 flex-grow w-0 truncate text-lg text-dark">联系客服</view>
|
|
|
|
|
<view class="flex-shrink-0">
|
|
|
|
|
<uni-icons type="right" size="18" color="#999"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="px-15 py-12 flex items-center border-b" @click="toPage('/pages/login/agreement?type=privacyPolicy')">
|
|
|
|
|
<view class="flex-shrink-0 item-icon">
|
|
|
|
|
<uni-icons type="locked" size="22" color="#000"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="mr-10 flex-grow w-0 truncate text-lg text-dark">隐私保护政策</view>
|
|
|
|
|
<view class="flex-shrink-0">
|
|
|
|
|
<uni-icons type="right" size="18" color="#999"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="px-15 py-12 flex items-center border-b" @click="toPage('/pages/login/agreement?type=userAgreement')">
|
|
|
|
|
<view class="flex-shrink-0 item-icon">
|
|
|
|
|
<uni-icons type="locked" size="22" color="#000"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="mr-10 flex-grow w-0 truncate text-lg text-dark">用户注册协议</view>
|
|
|
|
|
<view class="flex-shrink-0">
|
|
|
|
|
<uni-icons type="right" size="18" color="#999"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="px-15 py-12 flex items-center" @click="handleLogout()">
|
|
|
|
|
<view class="flex-shrink-0 item-icon">
|
|
|
|
|
<uni-icons type="undo" size="22" color="#000"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="mr-10 flex-grow w-0 truncate text-lg text-dark">退出登录</view>
|
|
|
|
|
<view class="flex-shrink-0">
|
|
|
|
|
<uni-icons type="right" size="18" color="#999"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
2026-02-02 11:20:15 +08:00
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-07-29 11:25:29 +08:00
|
|
|
import { computed, ref } from "vue";
|
|
|
|
|
import { onShow } from "@dcloudio/uni-app";
|
|
|
|
|
import { storeToRefs } from "pinia";
|
2026-02-02 11:20:15 +08:00
|
|
|
import accountStore from "@/store/account";
|
2026-07-29 11:25:29 +08:00
|
|
|
import api from "@/utils/api";
|
|
|
|
|
import { get } from "@/utils/cache";
|
|
|
|
|
import { normalizeCorpId } from "@/utils/api-base-config";
|
|
|
|
|
import { toast } from "@/utils/widget";
|
|
|
|
|
|
|
|
|
|
const HOME_CURRENT_TEAM_CACHE_KEY = "home-current-team-info";
|
2026-02-02 11:20:15 +08:00
|
|
|
|
|
|
|
|
const store = accountStore();
|
2026-07-29 11:25:29 +08:00
|
|
|
const { account } = storeToRefs(store);
|
|
|
|
|
const { getTeams, login } = store;
|
|
|
|
|
|
|
|
|
|
const context = ref({
|
|
|
|
|
corpId: "",
|
|
|
|
|
teamId: "",
|
|
|
|
|
customerId: "",
|
|
|
|
|
customerName: "",
|
|
|
|
|
customers: [],
|
|
|
|
|
});
|
|
|
|
|
const resolving = ref(false);
|
2026-02-02 11:20:15 +08:00
|
|
|
|
|
|
|
|
const maskedPhone = computed(() => {
|
2026-07-29 11:25:29 +08:00
|
|
|
const localAccount = uni.getStorageSync("account");
|
|
|
|
|
const mobile = localAccount?.mobile || account.value?.mobile || "";
|
|
|
|
|
if (!mobile) return "";
|
|
|
|
|
return mobile.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
|
2026-02-02 11:20:15 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleLogout = () => {
|
|
|
|
|
uni.showModal({
|
2026-07-29 11:25:29 +08:00
|
|
|
title: "提示",
|
|
|
|
|
content: "确定要退出登录吗?",
|
2026-02-02 11:20:15 +08:00
|
|
|
success: function (res) {
|
|
|
|
|
if (res.confirm) {
|
2026-07-29 11:25:29 +08:00
|
|
|
uni.removeStorageSync("account");
|
|
|
|
|
uni.removeStorageSync("openid");
|
2026-02-02 11:20:15 +08:00
|
|
|
store.account = null;
|
|
|
|
|
uni.reLaunch({
|
2026-07-29 11:25:29 +08:00
|
|
|
url: "/pages/login/login",
|
2026-02-02 11:20:15 +08:00
|
|
|
});
|
|
|
|
|
}
|
2026-07-29 11:25:29 +08:00
|
|
|
},
|
2026-02-02 11:20:15 +08:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-29 11:25:29 +08:00
|
|
|
function pickPreferredCustomer(list = []) {
|
|
|
|
|
if (!Array.isArray(list) || !list.length) return null;
|
|
|
|
|
return list.find((item) => item.relationship === "本人") || list[0] || null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function ensureLogin() {
|
|
|
|
|
if (account.value?.openid) return true;
|
|
|
|
|
const local = uni.getStorageSync("account");
|
|
|
|
|
if (local?.openid) {
|
|
|
|
|
store.account = local;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
const res = await login();
|
|
|
|
|
return !!(res && res.openid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function resolveTeam() {
|
|
|
|
|
const cached = get(HOME_CURRENT_TEAM_CACHE_KEY) || {};
|
|
|
|
|
let teams = [];
|
|
|
|
|
try {
|
|
|
|
|
teams = (await getTeams()) || [];
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn("getTeams failed", e);
|
|
|
|
|
}
|
|
|
|
|
const cachedCorpId = normalizeCorpId(cached.corpId || "");
|
|
|
|
|
const matched =
|
|
|
|
|
teams.find(
|
|
|
|
|
(item) =>
|
|
|
|
|
item.teamId === cached.teamId &&
|
|
|
|
|
(!cachedCorpId || normalizeCorpId(item.corpId || "") === cachedCorpId)
|
|
|
|
|
) ||
|
|
|
|
|
teams.find((item) => item.teamId === cached.teamId) ||
|
|
|
|
|
teams[0] ||
|
|
|
|
|
null;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
corpId: normalizeCorpId(matched?.corpId || cached.corpId || ""),
|
|
|
|
|
teamId: matched?.teamId || cached.teamId || "",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function resolveContext({ force = false } = {}) {
|
|
|
|
|
if (resolving.value) return context.value;
|
|
|
|
|
if (!force && context.value.corpId && context.value.customerId) {
|
|
|
|
|
return context.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resolving.value = true;
|
|
|
|
|
try {
|
|
|
|
|
const ok = await ensureLogin();
|
|
|
|
|
if (!ok) {
|
|
|
|
|
toast("请先登录");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const team = await resolveTeam();
|
|
|
|
|
if (!team.corpId) {
|
|
|
|
|
toast("请先加入服务团队");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const miniAppId = account.value?.openid || uni.getStorageSync("openid") || "";
|
|
|
|
|
const res = await api(
|
|
|
|
|
"getMiniAppCustomers",
|
|
|
|
|
{ miniAppId, corpId: team.corpId },
|
|
|
|
|
false
|
|
|
|
|
);
|
|
|
|
|
const customers = res?.success && Array.isArray(res.data) ? res.data : [];
|
|
|
|
|
const preferred = pickPreferredCustomer(customers);
|
|
|
|
|
|
|
|
|
|
context.value = {
|
|
|
|
|
corpId: team.corpId,
|
|
|
|
|
teamId: team.teamId,
|
|
|
|
|
customerId: preferred?._id || "",
|
|
|
|
|
customerName: preferred
|
|
|
|
|
? `${preferred.name || ""}${preferred.relationship ? ` ${preferred.relationship}` : ""}`.trim()
|
|
|
|
|
: "",
|
|
|
|
|
customers,
|
|
|
|
|
};
|
|
|
|
|
return context.value;
|
|
|
|
|
} finally {
|
|
|
|
|
resolving.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function goBindArchive(ctx) {
|
|
|
|
|
const corpId = ctx?.corpId || "";
|
|
|
|
|
const teamId = ctx?.teamId || "";
|
2026-02-09 15:17:45 +08:00
|
|
|
uni.navigateTo({
|
2026-07-29 11:25:29 +08:00
|
|
|
url: `/pages/archive/archive-manage?corpId=${encodeURIComponent(corpId)}&teamId=${encodeURIComponent(teamId)}`,
|
2026-02-09 15:17:45 +08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-29 11:25:29 +08:00
|
|
|
function openArchiveSelector(target, ctx) {
|
|
|
|
|
const params = [
|
|
|
|
|
`mode=target`,
|
|
|
|
|
`target=${encodeURIComponent(target)}`,
|
|
|
|
|
`corpId=${encodeURIComponent(ctx?.corpId || "")}`,
|
|
|
|
|
`teamId=${encodeURIComponent(ctx?.teamId || "")}`,
|
|
|
|
|
`customerId=${encodeURIComponent(ctx?.customerId || "")}`,
|
|
|
|
|
`name=${encodeURIComponent(ctx?.customerName || "")}`,
|
|
|
|
|
].join("&");
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
url: `/pages/experience-coupon/select-rights-archive?${params}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function openWithArchive(target) {
|
|
|
|
|
const ctx = await resolveContext({ force: true });
|
|
|
|
|
if (!ctx) return;
|
|
|
|
|
|
|
|
|
|
if (!ctx.customerId) {
|
|
|
|
|
uni.showModal({
|
|
|
|
|
title: "提示",
|
|
|
|
|
content: "请先绑定档案后再查看",
|
|
|
|
|
confirmText: "去绑定",
|
|
|
|
|
success: (res) => {
|
|
|
|
|
if (res.confirm) goBindArchive(ctx);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
openArchiveSelector(target, ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function openRightsWallet() {
|
|
|
|
|
await openWithArchive("rights");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function openCouponWallet() {
|
|
|
|
|
await openWithArchive("coupons");
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-29 17:30:30 +08:00
|
|
|
async function openTreatmentRecord() {
|
|
|
|
|
await openWithArchive("treatment");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function openAppointmentRecord() {
|
|
|
|
|
await openWithArchive("appointment");
|
2026-07-29 11:25:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toPage(url) {
|
|
|
|
|
uni.navigateTo({ url });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onShow(() => {
|
|
|
|
|
resolveContext({ force: true });
|
|
|
|
|
});
|
2026-02-02 11:20:15 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
page {
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-bg {
|
|
|
|
|
height: 50px;
|
|
|
|
|
background-color: #065bd6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-card {
|
|
|
|
|
position: relative;
|
|
|
|
|
margin: -40px 15px 15px;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-02-09 15:17:45 +08:00
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
2026-02-02 11:20:15 +08:00
|
|
|
|
|
|
|
|
.avatar-container {
|
|
|
|
|
width: 60px;
|
|
|
|
|
height: 60px;
|
|
|
|
|
background-color: #eee;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
margin-right: 15px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
2026-02-09 15:17:45 +08:00
|
|
|
|
2026-02-02 11:20:15 +08:00
|
|
|
.nickname {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #333;
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
2026-02-09 15:17:45 +08:00
|
|
|
|
2026-02-02 11:20:15 +08:00
|
|
|
.phone {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-29 11:25:29 +08:00
|
|
|
.shortcut-bar {
|
|
|
|
|
margin: 0 15px 15px;
|
|
|
|
|
padding: 12px 0 10px;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.shortcut-item {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.shortcut-icon {
|
|
|
|
|
width: 48px;
|
|
|
|
|
height: 48px;
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.shortcut-icon--rights {
|
|
|
|
|
background: #eef5ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.shortcut-icon--coupon {
|
|
|
|
|
background: #fff6e8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.shortcut-icon--record {
|
|
|
|
|
background: #ecfff5;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-29 17:30:30 +08:00
|
|
|
.shortcut-icon--appointment {
|
|
|
|
|
background: #f3edff;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-29 11:25:29 +08:00
|
|
|
.shortcut-title {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 11:20:15 +08:00
|
|
|
.menu-container {
|
|
|
|
|
background-color: #fff;
|
2026-07-29 11:25:29 +08:00
|
|
|
margin: 0 15px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
2026-02-02 11:20:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-icon {
|
2026-07-29 11:25:29 +08:00
|
|
|
width: 24px;
|
|
|
|
|
margin-right: 12px;
|
2026-02-09 15:17:45 +08:00
|
|
|
display: flex;
|
2026-07-29 11:25:29 +08:00
|
|
|
justify-content: center;
|
2026-02-02 11:20:15 +08:00
|
|
|
}
|
2026-07-29 11:25:29 +08:00
|
|
|
</style>
|