This commit is contained in:
Your Name
2026-05-27 18:11:26 +08:00
parent 13f58a5fdc
commit 4f066b560e
2 changed files with 163 additions and 20 deletions
+13
View File
@@ -778,6 +778,7 @@ const showDatePicker = ref(false)
const diagnosisId = ref(null)
const patientId = ref(null)
const isAddMode = ref(false) // 新建模式
const returnUrl = ref('') // 保存成功后跳回(如日常记录页)
// 表单数据
const formData = ref({
@@ -1023,6 +1024,13 @@ const loadCardDetail = async () => {
const options = currentPage.options || {}
diagnosisId.value = options.id
isAddMode.value = options.add === '1' || options.add === 1
if (options.returnUrl) {
try {
returnUrl.value = decodeURIComponent(String(options.returnUrl))
} catch (e) {
returnUrl.value = String(options.returnUrl)
}
}
const userData = uni.getStorageSync('userData')
patientId.value = userData?.diagnosis?.patient_id
@@ -1302,7 +1310,12 @@ const submitForm = async () => {
}
uni.showToast({ title: isAddMode.value ? '创建成功' : '保存成功', icon: 'success' })
setTimeout(() => {
const back = returnUrl.value
if (back && back.startsWith('/')) {
uni.redirectTo({ url: back })
} else {
uni.navigateBack()
}
}, 1500)
} else {
uni.showToast({ title: res.msg || '保存失败', icon: 'none' })
+149 -19
View File
@@ -212,7 +212,11 @@
</view>
</view>
<block v-if="!isInviteViewer">
<view v-if="authChecking && !isInviteViewer" class="page-gate-loading">
<text class="page-gate-loading-text">正在登录</text>
</view>
<block v-if="!isInviteViewer && !authChecking">
<!-- 顶部 Hero智能问候 + 今日状态-->
<view class="hero">
<view class="hero-bg" />
@@ -1384,6 +1388,10 @@ const patientName = ref('')
const age = ref(0)
const gender = ref(0)
/** 登录 / 就诊卡门禁校验中(避免未登录时闪屏) */
const authChecking = ref(false)
let gateRedirected = false
const loading = ref(false)
const cardsLoading = ref(false)
const cards = ref([])
@@ -1767,10 +1775,104 @@ function getDayBpTrend(date) {
return makeTrend(cur, prev.systolic, 0)
}
// ============ 登录 / 就诊卡门禁 ============
/** 进行中的登录任务(避免与 App.onLaunch 重复发起 mnpLogin,并保证业务接口在其之后) */
let authSessionPromise = null
function hasAuthToken() {
return !!String(uni.getStorageSync('token') || '').trim()
}
function clearAuthStorage() {
uni.removeStorageSync('token')
authSessionPromise = null
}
function wxLoginGetCode() {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (res) => {
if (res && res.code) resolve(res.code)
else reject(new Error('微信登录未返回 code'))
},
fail: reject
})
})
}
async function mnpLoginWithCode(code) {
const res = await proxy.apiUrl({
url: '/api/login/mnpLogin',
method: 'POST',
data: { code }
}, false)
if (res && res.code === 1 && res.data && res.data.token) {
uni.setStorageSync('token', res.data.token)
uni.setStorageSync('userData', res.data)
return res.data
}
clearAuthStorage()
throw new Error(formatUserMessage(res?.msg, '登录失败'))
}
async function doWxLogin() {
const code = await wxLoginGetCode()
await mnpLoginWithCode(code)
return hasAuthToken()
}
/** 校验 token 有效;无效则清除并重新 mnpLogin(缺 token 时后端 code=0,不能当作已登录) */
async function verifyOrLogin() {
if (!hasAuthToken()) {
return doWxLogin()
}
try {
const res = await proxy.apiUrl({ url: '/api/user/info', method: 'POST' }, false)
if (res && res.code === 1 && res.data) {
uni.setStorageSync('userData', res.data)
return true
}
} catch (e) {
/* 网络异常走重新登录 */
}
clearAuthStorage()
try {
return await doWxLogin()
} catch (e2) {
return false
}
}
/** 登录完成后再调业务接口;并发调用共享同一 Promise */
async function ensureLoggedIn() {
if (authSessionPromise) {
return authSessionPromise
}
authSessionPromise = verifyOrLogin()
.then((ok) => {
if (!ok) authSessionPromise = null
return !!ok
})
.catch(() => {
authSessionPromise = null
return false
})
return authSessionPromise
}
function redirectToCardEntry() {
if (gateRedirected) return
gateRedirected = true
const returnUrl = encodeURIComponent('/tongji/pages/index')
uni.redirectTo({
url: `/pages/Card/edit_card?add=1&returnUrl=${returnUrl}`
})
}
// ============ 就诊卡 ============
async function fetchCardList() {
const token = uni.getStorageSync('token')
if (!token) return
if (!hasAuthToken()) return []
cardsLoading.value = true
try {
const res = await proxy.apiUrl({
@@ -1793,7 +1895,7 @@ async function fetchCardList() {
}
async function applyCard(card) {
if (!card) return
if (!card || !hasAuthToken()) return
diagnosisId.value = Number(card.id) || 0
patientId.value = Number(card.patient_id) || 0
patientName.value = card.patient_name || ''
@@ -1822,7 +1924,7 @@ async function onSelectCard(card) {
// ============ 接口请求 ============
async function fetchAll() {
if (!diagnosisId.value) return
if (!hasAuthToken() || !diagnosisId.value) return
loading.value = true
const { start, end } = currentRange.value
try {
@@ -3095,24 +3197,33 @@ async function bootstrap(options) {
options = options || {}
const inviteCode = String(options.invite_code || '').trim()
if (options.from === 'share' && inviteCode) {
authChecking.value = false
await bootstrapInviteViewer(inviteCode)
return
}
isInviteViewer.value = false
const passedDiagnosisId = Number(options.diagnosis_id || options.id || 0)
diagnosisId.value = passedDiagnosisId
patientId.value = Number(options.patient_id || 0)
if (options.patient_name) patientName.value = decodeURIComponent(options.patient_name)
if (options.age) age.value = Number(options.age) || 0
if (options.gender) gender.value = Number(options.gender) || 0
gateRedirected = false
authChecking.value = true
const token = uni.getStorageSync('token')
if (!token) {
showUserToast('请先登录')
// 登录完成前不写 diagnosisId,避免模板/子流程提前打业务接口
const passedDiagnosisId = Number(options.diagnosis_id || options.id || 0)
const passedPatientId = Number(options.patient_id || 0)
const passedPatientName = options.patient_name
? decodeURIComponent(String(options.patient_name))
: ''
const passedAge = Number(options.age) || 0
const passedGender = Number(options.gender) || 0
const loggedIn = await ensureLoggedIn()
if (!loggedIn || !hasAuthToken()) {
authChecking.value = false
showUserToast('登录失败,请稍后重试')
return
}
readUserContext()
const list = await fetchCardList()
if (Array.isArray(list) && list.length > 0) {
@@ -3123,16 +3234,23 @@ async function bootstrap(options) {
if (!target) target = list[0]
await applyCard(target)
} else {
if (passedDiagnosisId) diagnosisId.value = passedDiagnosisId
if (passedPatientId) patientId.value = passedPatientId
if (passedPatientName) patientName.value = passedPatientName
if (passedAge) age.value = passedAge
if (passedGender) gender.value = passedGender
if (!diagnosisId.value || !patientId.value) {
readUserContext()
}
if (!diagnosisId.value) {
showUserToast('暂无就诊卡,请先创建')
authChecking.value = false
redirectToCardEntry()
return
}
}
fetchAll()
authChecking.value = false
await fetchAll()
}
onLoad((options) => {
@@ -3152,6 +3270,7 @@ onShareAppMessage(() => {
})
onShow(() => {
if (authChecking.value || gateRedirected) return
if (!isInviteViewer.value && diagnosisId.value) {
loadFamilyLikeSummary()
}
@@ -3162,7 +3281,7 @@ onPullDownRefresh(async () => {
if (isInviteViewer.value) {
const code = invitePreview.value.invite_code
if (code) await bootstrapInviteViewer(code)
} else {
} else if (await ensureLoggedIn()) {
await fetchAll()
}
uni.stopPullDownRefresh()
@@ -3215,7 +3334,7 @@ function applyGamifyPayload(data) {
}
async function fetchGamifyState() {
if (!diagnosisId.value) return
if (!hasAuthToken() || !diagnosisId.value) return
try {
const res = await proxy.apiUrl({
url: '/api/tcm/dailyGetGamify',
@@ -3725,7 +3844,7 @@ function closeWeeklyReport() {
}
async function loadFamilyLikeSummary() {
if (!diagnosisId.value || isInviteViewer.value) return
if (!hasAuthToken() || !diagnosisId.value || isInviteViewer.value) return
const token = uni.getStorageSync('token')
if (!token) return
try {
@@ -3846,6 +3965,17 @@ async function onFamilyLike() {
</script>
<style lang="scss" scoped>
.page-gate-loading {
min-height: 60vh;
display: flex;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
}
.page-gate-loading-text {
font-size: 28rpx;
color: #64748b;
}
.daily-page {
/* Premium UI Color System & Variables - Teal-Emerald Healing Redesign */
--primary: #0d9488;