400 lines
10 KiB
Vue
Raw Normal View History

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>