fix: 首页UI重构设计

This commit is contained in:
2026-01-12 12:36:50 +08:00
parent 1251897013
commit 2a294898ec
13 changed files with 706 additions and 161 deletions

52
App.vue
View File

@@ -1,11 +1,14 @@
<script>
import {
getVersion
getVersion,
getSystemConfig
} from './request/api/api'
import { webSocketManager } from '@/utils/ws/websocket.js';
import { ChatManager } from '@/store/chat/chat.js';
import {checkDev} from "@/utils/utils";
import {bindUserApi} from "@/request/api/salesperson";
import { silentLogin } from '@/utils/login.js';
import { isGuestMode } from '@/common/utils/auth.js';
export default {
// //初始化状态-如果登录过,保持登录状态
onLaunch: async function() {
@@ -31,7 +34,54 @@
// 存储当前版本,也可以使用globalData
uni.setStorageSync('isShow_envVersion', isShow)
// 检查是否已登录,如果未登录才检查配置并决定是否静默登录
const token = uni.getStorageSync('token');
const userInfo = uni.getStorageSync('userinfo');
if (!token || !userInfo) {
// 未登录,获取系统配置,判断是否需要静默登录
try {
const configRes = await getSystemConfig({
method: "get",
data: {
store_id: uni.getStorageSync('store_id') || '11001'
}
});
// 根据配置决定是否静默登录
// 注意:需要根据实际接口返回格式解析 need_login 字段
const isGuest = isGuestMode();
let needLogin = true; // 默认需要登录页
if (isGuest && (configRes.data.code === 0 || configRes.data.code === '0')) {
// 新格式:从 result 中获取
const configData = configRes.data.result || {};
needLogin = configData.need_login !== false; // false 表示静默登录
} else if (!isGuest && (configRes.data.errcode === 0 || configRes.data.errcode === '0')) {
// 旧格式:从 data 中获取
const configData = configRes.data.data || {};
needLogin = configData.need_login !== false; // false 表示静默登录
}
// 如果配置为静默登录need_login: false直接执行静默登录
if (!needLogin) {
// 执行静默登录(不显示提示,不跳转)
silentLogin().then((loginResult) => {
if (loginResult.success) {
console.log('静默登录成功');
} else {
console.error('静默登录失败:', loginResult.message);
}
}).catch((error) => {
console.error('静默登录异常:', error);
});
}
// 如果配置为需要登录页need_login: true不执行任何操作等待用户手动登录
} catch (error) {
// 配置接口调用失败,不影响应用启动,仅记录日志
console.error('获取系统配置失败:', error);
}
}
console.log('获取更新机制', uni.canIUse('getUpdateManager'))
// 获取小程序更新机制兼容

109
common/utils/auth.js Normal file
View File

@@ -0,0 +1,109 @@
/**
* 未登录体验模式Guest Mode工具函数
* 用于管理未登录用户的访问权限和跳转信息
*/
const GUEST_MODE_KEY = 'guest_mode';
const GUEST_STORE_ID = '11001';
const REDIRECT_INFO_KEY = 'redirect_info';
/**
* 判断是否为未登录体验模式
* @returns {boolean}
*/
export function isGuestMode() {
return uni.getStorageSync(GUEST_MODE_KEY) === true;
}
/**
* 设置未登录体验模式标记
*/
export function setGuestMode() {
uni.setStorageSync(GUEST_MODE_KEY, true);
// 同时设置store_id为11001
uni.setStorageSync('store_id', GUEST_STORE_ID);
}
/**
* 清除未登录体验模式标记
*/
export function clearGuestMode() {
uni.removeStorageSync(GUEST_MODE_KEY);
}
/**
* 获取Guest模式下的固定诊所ID
* @returns {string}
*/
export function getGuestStoreId() {
return GUEST_STORE_ID;
}
/**
* 保存跳转登录前的页面路径和参数
* @param {string} path - 页面路径
* @param {object} params - 页面参数对象
*/
export function saveRedirectInfo(path, params = {}) {
const redirectInfo = {
path,
params,
timestamp: Date.now()
};
uni.setStorageSync(REDIRECT_INFO_KEY, redirectInfo);
}
/**
* 获取保存的跳转信息
* @returns {object|null} 返回 {path, params} 或 null
*/
export function getRedirectInfo() {
const info = uni.getStorageSync(REDIRECT_INFO_KEY);
if (!info) {
return null;
}
// 检查是否过期24小时
const expireTime = 24 * 60 * 60 * 1000; // 24小时
if (Date.now() - info.timestamp > expireTime) {
clearRedirectInfo();
return null;
}
return {
path: info.path,
params: info.params
};
}
/**
* 清除跳转信息
*/
export function clearRedirectInfo() {
uni.removeStorageSync(REDIRECT_INFO_KEY);
}
/**
* 检查是否已登录
* @returns {boolean}
*/
export function isLoggedIn() {
const token = uni.getStorageSync('token');
const userInfo = uni.getStorageSync('userinfo');
return !!(token && userInfo);
}
/**
* 构建带参数的URL
* @param {string} path - 页面路径
* @param {object} params - 参数对象
* @returns {string}
*/
export function buildUrlWithParams(path, params = {}) {
if (!params || Object.keys(params).length === 0) {
return path;
}
const queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
return `${path}?${queryString}`;
}

View File

@@ -8,7 +8,16 @@
"^MessageNotification": "@/components/MessageNotification/MessageNotification.vue"
},
"pages": [
//pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/home/home",
"style": {
// "navigationBarTitleText": "custom",
"navigationStyle": "custom"
// "navigationStyle": "default",
// "navigationBarBackgroundColor": "#298DFF",
// "navigationBarTextStyle": "white"
}
},
{
"path": "pages/home/index",
"style": {
@@ -19,16 +28,6 @@
},
"needLogin": false
},
{
"path": "pages/home/home",
"style": {
// "navigationBarTitleText": "custom",
"navigationStyle": "custom"
// "navigationStyle": "default",
// "navigationBarBackgroundColor": "#298DFF",
// "navigationBarTextStyle": "white"
}
},
{
"path": "pages/home/index-list",
"style": {

View File

@@ -74,7 +74,7 @@
<view class="list_nums">
<view class="txt">问诊量 <text>{{item.inquiry_num }}</text></view>
<view class="txt">接诊率 <text>{{item.accept_percent}}</text></view>
<!-- <view class="txt">综合评分 <text>{{item.overall_score}}</text></view> -->
<view class="txt">综合评分 <text>{{item.overall_score}}</text></view>
</view>
</view>
</view>
@@ -129,6 +129,7 @@
import {
dDropdown
} from '@/components/d-dropdown/d-dropdown'
import { isGuestMode } from '@/common/utils/auth.js'
export default {
components: {
dDropdown
@@ -272,8 +273,16 @@
}
}).then((res) => {
// console.log(res, 'res');
if (res.data.errcode == 0) {
this.doctorList = res.data.data.list
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.doctorList = responseData.list || []
}
})
},
@@ -287,8 +296,16 @@
}
}).then((res) => {
// console.log(res, 'ks');
if (res.data.errcode == 0) {
this.officeList = res.data.data
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.officeList = responseData || [];
} else if (res.data.errcode != 0) {
// uni.showToast({
// title: res.data.msg,

View File

@@ -6,7 +6,7 @@
<!-- 1. 自定义吸顶导航栏 -->
<view class="custom-nav"
:class="[
storeType === '1' ? 'header-pharmacy' : 'header-clinic',
(storeType === '1' || storeType === 1) ? 'header-pharmacy' : 'header-clinic',
{ 'nav-sticky': isSticky }
]"
:style="{ paddingTop: statusBarHeight + 'px', height: (statusBarHeight + 44) + 'px' }">
@@ -14,7 +14,7 @@
<view class="nav-content">
<view class="store-info" @click="toStore">
<text class="store-name u-line-1">{{infoList.store['name'] || storeinfo[0].store['name'] || info.name || '请选择门店'}}</text>
<view class="switch-badge">
<view v-if="!isGuestMode()" class="switch-badge">
<text class="iconfont icon-qiehuan"></text> 切换
</view>
</view>
@@ -22,7 +22,7 @@
</view>
<!-- 2. 头部背景与定位区域 -->
<view class="header" :class="storeType === '1' ? 'header-pharmacy' : 'header-clinic'"
<view class="header" :class="(storeType === '1' || storeType === 1) ? 'header-pharmacy' : 'header-clinic'"
:style="{ paddingTop: (statusBarHeight + 44 + 10) + 'px' }">
<view class="bg-circle"></view>
<view class="location-bar">
@@ -52,7 +52,7 @@
<view v-for="(item, index) in homeZones" :key="index" class="zone-item" @click="goZoneList(item)">
<view class="zone-icon-box">
<image
:src="item.icon || (storeType === '1' ? 'https://img.icons8.com/fluency/48/pill.png' : 'https://img.icons8.com/fluency/48/doctor-male.png')"
:src="item.icon || ((storeType === '1' || storeType === 1) ? 'https://img.icons8.com/fluency/48/pill.png' : 'https://img.icons8.com/fluency/48/doctor-male.png')"
class="zone-img"
mode="aspectFit"
></image>
@@ -64,7 +64,7 @@
<!-- 3. 核心功能导航 (Nav) -->
<view class="nav-grid">
<!-- 诊所模式2列布局 -->
<view v-if="storeType === '0'" class="nav-clinic-grid">
<view v-if="storeType === '0' || storeType === 0" class="nav-clinic-grid">
<view class="nav-card" @click="goYard(1)">
<view class="nav-content">
<view class="nav-title" style="color:#4175EE">到店挂号</view>
@@ -82,7 +82,7 @@
</view>
<!-- 药店模式单列铺满 -->
<view v-if="storeType === '1'" class="nav-pharmacy-grid">
<view v-if="storeType === '1' || storeType === 1" class="nav-pharmacy-grid">
<view class="nav-card" @click="golist(3)">
<view class="nav-content">
<view class="nav-title" style="color:#298DFF">预约购药</view>
@@ -94,7 +94,7 @@
</view>
<!-- 4. 诊所模式内容医生列表 -->
<view v-if="storeType === '0'">
<view v-if="storeType === '0' || storeType === 0">
<view class="section-header">
<view class="section-title">医生推荐</view>
<view class="section-more" @click="goYard(1)">更多 <text class="iconfont icon-jiantou1" style="font-size: 20rpx;"></text></view>
@@ -129,7 +129,7 @@
<view class="stat-label">接诊率</view>
</view>
<view class="stat-item">
<text class="stat-num">10分钟</text>
<text class="stat-num">{{item.average_response_time || 5}}分钟</text>
<view class="stat-label">平均响应</view>
</view>
</view>
@@ -137,7 +137,7 @@
</view>
<!-- 5. 药店模式内容热销好药 -->
<view v-if="storeType === '1'">
<view v-if="storeType === '1' || storeType === 1">
<view class="section-header">
<view class="section-title">{{ topProductsTitle || '热销好药' }}</view>
<view class="section-more" @click="golist(3)">更多 <text class="iconfont icon-jiantou1" style="font-size: 20rpx;"></text></view>
@@ -212,6 +212,7 @@ import { getPlatformQualificationsApi } from '../../request/api/platform'
import { getStoreDrugListBySalespersonApi, getHomeTopProductsApi, getHomeZonesApi, checkOnlineConsultationConfigApi } from "@/request/api/product";
import request from "@/request/api/request";
import MessageNotification from "@/components/MessageNotification/MessageNotification.vue";
import { setGuestMode, isGuestMode, isLoggedIn } from '@/common/utils/auth.js';
export default {
components: {
@@ -250,16 +251,19 @@ export default {
// 解决 template 中复杂表达式导致的渲染错误
displayProductList() {
return this.topProductsList.length > 0 ? this.topProductsList : this.productList;
},
// 判断是否为guest模式用于模板
isGuestMode() {
return isGuestMode;
}
},
onLoad() {
const token = uni.getStorageSync('token')
const userInfo = uni.getStorageSync('userinfo')
if (!token || !userInfo) {
uni.navigateTo({
url: '/pages/home/index'
})
// 判断是否已登录
if (!isLoggedIn()) {
// 未登录时设置guest模式标记和store_id
setGuestMode();
}
this.isShow_envVersion = uni.getStorageSync('isShow_envVersion')
if (this.isShow_envVersion === false) {
this.getList();
@@ -273,6 +277,10 @@ export default {
}
},
onReady() {
// 未登录时也允许访问使用默认store_id=11001
if (!uni.getStorageSync('store_id')) {
uni.setStorageSync('store_id', '11001');
}
if (uni.getStorageSync('store_id')) {
this.getStore();
} else {
@@ -353,12 +361,23 @@ export default {
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
if (res.data.errcode == 0) {
this.storeType = res.data.data.store['type']
uni.setStorageSync('store_type', res.data.data.store['type']) // 存储门店类型
this.infoList = res.data.data
this.headerTitle = res.data.data.store['name']
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
if (responseData && responseData.store) {
this.storeType = responseData.store['type']
uni.setStorageSync('store_type', responseData.store['type']) // 存储门店类型
this.infoList = responseData
this.headerTitle = responseData.store['name']
this.getBannered();
}
}
})
} else {
@@ -383,7 +402,9 @@ export default {
this.getBannered();
}
} else {
uni.navigateTo({ url: '/pages/home/index' })
// 未登录时不跳转使用默认store_id=11001
uni.setStorageSync('store_id', '11001');
this.getBannered();
}
})
},
@@ -393,8 +414,16 @@ export default {
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
if (res.data.errcode === 0) {
this.doctorList = (res.data.data.list).slice(0, 5)
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.doctorList = (responseData.list || []).slice(0, 5)
}
})
},
@@ -411,11 +440,22 @@ export default {
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
if (res.data.errcode == 0) {
this.info = res.data.data
this.urlInfo = res.data.data.nav
this.storeType = res.data.data.type
this.list = (res.data.data.nav).map((item) => { return item.pic })
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
if (responseData) {
this.info = responseData
this.urlInfo = responseData.nav || []
this.storeType = responseData.type
this.list = (responseData.nav || []).map((item) => { return item.pic })
}
}
})
},

View File

@@ -130,7 +130,7 @@
<view class="list_nums">
<view class="txt">问诊量 <text>{{item.inquiry_num }}</text></view>
<view class="txt">接诊率 <text>{{item.accept_percent}}</text></view>
<!-- <view class="txt">综合评分 <text>{{item.overall_score}}</text></view> -->
<view class="txt">综合评分 <text>{{item.overall_score}}</text></view>
</view>
</view>
</view>
@@ -146,6 +146,7 @@
getBanner,
getOpen
} from '../../request/api/api'
import { isGuestMode } from '@/common/utils/auth.js'
export default {
data() {
return {
@@ -208,8 +209,16 @@
}
}).then((res) => {
// console.log(res, 'list');
if (res.data.errcode == 0) {
this.doctorList = (res.data.data.list).slice(0, 3)
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.doctorList = (responseData.list || []).slice(0, 3)
}
})
this.getBannered()

View File

@@ -65,6 +65,8 @@ import {
getsLogin, storeChange
} from '../../request/api/api.js'
import {bindUserApi} from "@/request/api/salesperson";
import { clearGuestMode, getRedirectInfo, clearRedirectInfo, buildUrlWithParams } from '@/common/utils/auth.js';
import { performLogin } from '@/utils/login.js';
export default {
data() {
@@ -95,8 +97,8 @@ export default {
},
methods: {
back() {
uni.navigateTo({
url: "/pages/home/index",
uni.reLaunch({
url: "/pages/home/home",
fail: (e) => {
// console.log(e);
},
@@ -162,69 +164,15 @@ export default {
})
},
getToLogin(e) {
uni.showLoading({
title: "登录中"
async getToLogin(e) {
// 使用抽离的登录函数,非静默模式(显示提示,执行跳转)
const result = await performLogin({
silent: false,
toastRef: this.$refs.uToast
});
uni.login({
provider: 'weixin',
success: (loginRes) => {
this.code = loginRes.code
getsLogin({
method: "post",
data: {
code: this.code,
status: 1
}
}).then((res) => {
uni.hideLoading();
if (res.data.errcode == 0) {
uni.setStorageSync('token', res.data.data.token) //存储token
uni.setStorageSync('user_id', res.data.data.data.user.id)
uni.setStorageSync('userinfo', res.data.data.data['user'])
if (uni.getStorageSync('salesperson_id')) {
bindUserApi({
user_id: res.data.data.data.user.id,
salesperson_id: uni.getStorageSync('salesperson_id'),
}).then((res) => {
console.log(res, 'bindUserApi', res);
})
}
if (uni.getStorageSync('store_id')) {
storeChange({
method: "post",
data: {
store_id: uni.getStorageSync('store_id')
}
})
} else {
uni.setStorageSync('store_id', res.data.data.data.store_id)
}
if (res.data.data.data.user.mobile != '') {
setTimeout(function () {
uni.reLaunch({
url: '/pages/home/home'
})
}, 500)
} else {
// this.getPhoneNumber(e);
}
} else if (res.data.errcode != 0) {
this.$refs.uToast.show({
title: res.data.msg,
type: 'default',
icon: false
})
}
})
}
})
// 登录成功后的处理已在 performLogin 中完成
// 这里可以添加额外的处理逻辑(如果需要)
},
},
mounted() {

View File

@@ -491,6 +491,11 @@ export async function clearAll(params) {
export async function getVersion(params) {
let data = await http('/v1/other/xcx-version?store_id=11001', params)
return data
}
// 获取系统配置信息
export async function getSystemConfig(params) {
let data = await http('/xkApi/platform/config', params)
return data
}

View File

@@ -1,5 +1,6 @@
import {Base64} from "js-base64";
import {checkDev} from "@/utils/utils";
import {isGuestMode, getGuestStoreId} from "@/common/utils/auth.js";
const arr = [
@@ -61,6 +62,79 @@ function request(url, params, method = 0) {
const envUrls = getEnvUrls();
let baseURL = envUrls.main;
// 判断是否为guest模式且未登录
const guestMode = isGuestMode();
const token = uni.getStorageSync("token");
const isGuestRequest = guestMode && !token;
// 如果是guest模式且是xkApi请求转换为guest路由
if (isGuestRequest && url.indexOf('/xkApi') !== -1) {
if (url.indexOf('/xkApi/online-consultation/get-default-doctor-id') !== -1) {
url = url.replace('/xkApi/online-consultation/get-default-doctor-id', '/guest/guest-home/get-default-doctor-id');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/product/drug-list-by-salesperson') !== -1) {
url = url.replace('/xkApi/product/drug-list-by-salesperson', '/guest/guest-product/drug-list-by-salesperson');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/product/home-top-products') !== -1) {
url = url.replace('/xkApi/product/home-top-products', '/guest/guest-product/home-top-products');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/product/home-zones') !== -1) {
url = url.replace('/xkApi/product/home-zones', '/guest/guest-product/home-zones');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/platform/config') !== -1) {
url = url.replace('/xkApi/platform/config', '/guest/guest-platform/config');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/platform/qualifications') !== -1) {
url = url.replace('/xkApi/platform/qualifications', '/guest/guest-platform/qualifications');
baseURL = envUrls.xkApi;
}
}
// 如果是guest模式且是oldApi请求转换为guest路由需要在替换之前处理
if (isGuestRequest && url.indexOf('/oldApi') !== -1) {
// 将 /oldApi/v1/xxx 转换为 /guest/guest-xxx/xxx
// 注意:路由前缀是 api/mobile所以完整路径是 api/mobile/guest/guest-doctor/list
if (url.indexOf('/oldApi/v1/doctor/all-list') !== -1) {
url = url.replace('/oldApi/v1/doctor/all-list', '/guest/guest-doctor/list');
baseURL = envUrls.xkApi; // guest路由使用xkApi的baseURL
} else if (url.indexOf('/oldApi/v1/doctor/detail') !== -1) {
url = url.replace('/oldApi/v1/doctor/detail', '/guest/guest-doctor/detail');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/doctor/comment-list') !== -1) {
url = url.replace('/oldApi/v1/doctor/comment-list', '/guest/guest-doctor/comment-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/patient/list') !== -1) {
url = url.replace('/oldApi/v1/patient/list', '/guest/guest-patient/list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/store/info') !== -1) {
url = url.replace('/oldApi/v1/store/info', '/guest/guest-home/store-info');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/store/detail') !== -1) {
url = url.replace('/oldApi/v1/store/detail', '/guest/guest-home/store-detail');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/register/doc-register-info') !== -1) {
url = url.replace('/oldApi/v1/register/doc-register-info', '/guest/guest-doctor/doc-register-info');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/doctor/office-list') !== -1) {
url = url.replace('/oldApi/v1/doctor/office-list', '/guest/guest-doctor/office-list');
baseURL = envUrls.xkApi;
}
}
// 如果是guest模式且是member/v1请求也需要转换为guest路由
if (isGuestRequest && (url.indexOf('/member/v1') !== -1 || url.indexOf('/v1/') !== -1)) {
if (url.indexOf('/v1/doctor/comment-list') !== -1 || url.indexOf('/member/v1/doctor/comment-list') !== -1) {
url = url.replace(/\/member\/v1\/doctor\/comment-list|\/v1\/doctor\/comment-list/, '/guest/guest-doctor/comment-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/v1/patient/list') !== -1 || url.indexOf('/member/v1/patient/list') !== -1) {
url = url.replace(/\/member\/v1\/patient\/list|\/v1\/patient\/list/, '/guest/guest-patient/list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/v1/register/doc-register-info') !== -1 || url.indexOf('/member/v1/register/doc-register-info') !== -1) {
url = url.replace(/\/member\/v1\/register\/doc-register-info|\/v1\/register\/doc-register-info/, '/guest/guest-doctor/doc-register-info');
baseURL = envUrls.xkApi;
}
}
if (url.split('/').indexOf('imApi') !== -1) {
baseURL = envUrls.im;
}
@@ -69,6 +143,10 @@ function request(url, params, method = 0) {
}
if (url.split('/').indexOf('xkApi') !== -1) {
baseURL = envUrls.xkApi;
// 如果是guest模式且是xkApi请求添加/guest/前缀
if (isGuestRequest) {
url = url.replace('/xkApi', '/guest/xkApi');
}
}
url = url.replace('/oldApi', '');
url = url.replace('/newApi', '');
@@ -77,7 +155,11 @@ function request(url, params, method = 0) {
// 加密
if (params != null) {
params.data = getRes(params.data, false)
params.data = getRes(params.data, false);
// 如果是guest模式强制设置store_id为11001
if (isGuestRequest && params.data) {
params.data.store_id = getGuestStoreId();
}
}
let num = 0;
// promise导出
@@ -98,13 +180,17 @@ function request(url, params, method = 0) {
// 成功钩子
success(res) {
if (res.data.errcode === 401) {
// 获取当前页面,如果不是 /pages/home/index
if (getCurrentPages()[getCurrentPages().length - 1].route !== 'pages/home/index') {
const currentPage = getCurrentPages()[getCurrentPages().length - 1];
const currentRoute = currentPage ? currentPage.route : '';
const isLoginPage = currentRoute === 'pages/login/login';
// 如果当前不在登录页,则跳转到登录页
if (!isLoginPage) {
uni.removeStorageSync('token')
uni.removeStorageSync('userinfo')
uni.removeStorageSync('token_h5')
uni.removeStorageSync('user_id')
uni.reLaunch({url: '/pages/home/index'})
uni.reLaunch({url: '/pages/login/login'})
}
}
if (res.data?.data != null) {

View File

@@ -22,7 +22,7 @@
<view class="card_center_top">
<view class="txt">问诊量 <text>{{doctorList.inquiry_num}}</text></view>
<view class="txt">接诊率<text>{{doctorList.accept_percent}}</text></view>
<!-- <view class="txt">综合评分 <text>{{doctorList.overall_score}}</text></view> -->
<view class="txt">综合评分 <text>{{doctorList.overall_score}}</text></view>
</view>
<view class="going">
专业擅长{{doctorList.good_at}}
@@ -45,8 +45,8 @@
<view class="times">{{registList.time}}</view>
<text class="price">{{registList.register_price}}</text>
</view>
<view class="btn" :class="registList.left_num<=0?'active':''" @click="goAdd">
{{registList.left_num>0&&'挂号' || registList.left_num<=0&&'约满'}}
<view class="btn" :class="(registList.left_num || 0) <= 0 ? 'active' : ''" @click="goAdd">
{{(registList.left_num || 0) > 0 ? '挂号' : '约满'}}
</view>
</view>
<view class="yard">
@@ -104,6 +104,7 @@
storeChange,
userList
} from '@/request/api/api'
import { isGuestMode, isLoggedIn, saveRedirectInfo, buildUrlWithParams } from '@/common/utils/auth.js';
export default {
data() {
return {
@@ -124,12 +125,10 @@
},
onLoad(e) {
console.log(e, 'e');
const token = uni.getStorageSync('token')
if (!token) {
uni.navigateTo({
url: '/pages/home/index'
})
return
// 允许未登录访问,移除登录跳转
// 如果是guest模式强制设置store_id为11001
if (isGuestMode()) {
uni.setStorageSync('store_id', '11001');
}
// 扫码之后
if (e.scene) {
@@ -156,11 +155,18 @@
// 点击详情
this.doctorID = e.id
this.rType = e.r_type || '0' // 接收挂号类型参数
// 未登录时强制使用11001
if (isGuestMode()) {
uni.setStorageSync('store_id', '11001');
this.getDocdetail();
this.getRegist();
} else {
const id = e.sts_id || uni.getStorageSync('store_id')
if (id!='undefined' && id) {
console.log('idddd');
this.toChang(id)
uni.setStorageSync('store_id', id)
}
}
}
},
@@ -173,6 +179,24 @@
},
// 挂号
goAdd() {
// 如果未登录,保存跳转信息并跳转到登录页
if (!isLoggedIn()) {
const storeId = uni.getStorageSync('store_id') || '11001';
saveRedirectInfo('/subPackages/doctor/doctor-detail', {
id: this.doctorID,
store_id: storeId,
r_type: this.rType
});
uni.navigateTo({
url: buildUrlWithParams('/pages/login/login', {
redirect: 'doctor-detail',
doctor_id: this.doctorID,
store_id: storeId
})
});
return;
}
if (this.num == 0) {
uni.showToast({
title: '暂无号源',
@@ -211,18 +235,18 @@
}
}).then((res) => {
// console.log(res, 'ress');
if (res.data.errcode == 0) {
this.doctorList = res.data.data
this.id = res.data.data.id
} else if (res.data.errcode != 0) {
// uni.showToast({
// title: res.data.msg,
// duration: 1500,
// mask: false,
// icon: 'error'
// })
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.doctorList = responseData;
this.id = responseData.id;
} else {
const errorMsg = isGuest ? res.data.message : res.data.msg;
this.$refs.uToast.show({
title: res.data.msg,
title: errorMsg || '获取失败',
type: 'default',
icon: false
})
@@ -240,8 +264,13 @@
}
}).then((res) => {
// console.log(res, 'user');
if (res.data.errcode == 0) {
this.userList = (res.data.data.list).slice(0, 3)
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.userList = (responseData.list || []).slice(0, 3);
}
})
},
@@ -256,9 +285,21 @@
}
}).then((res) => {
// console.log(res, 'reg');
if (res.data.errcode == 0) {
this.registList = res.data.data
this.num = res.data.data.left_num
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
// 确保所有字段都有默认值,避免显示 undefined
this.registList = {
time: responseData.time || '',
register_price: responseData.register_price || 0,
left_num: parseInt(responseData.left_num) || 0,
register_num: parseInt(responseData.register_num) || 0,
register_status: responseData.register_status || 1
};
this.num = this.registList.left_num;
}
})
},
@@ -272,8 +313,13 @@
}
}).then((res) => {
// console.log(res, 'reg');
if (res.data.errcode == 0) {
this.peopleList = res.data.data
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.peopleList = responseData.list || responseData || [];
}
})
},

View File

@@ -55,6 +55,7 @@
<script>
import { doctorDetail } from '../../request/api/api'
import { isGuestMode } from '@/common/utils/auth.js';
export default {
data() {
return {
@@ -77,20 +78,19 @@ import { doctorDetail } from '../../request/api/api'
}
}).then((res) => {
// console.log(res, 'ress');
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (res.data.errcode == 0) {
this.doctorList = res.data.data
this.id = res.data.data.id
if (isNewFormat || isOldFormat) {
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.doctorList = responseData;
this.id = responseData.id;
// console.log(this.doctorList, 'list');
} else if (res.data.errcode != 0) {
// uni.showToast({
// title: res.data.msg,
// duration: 1500,
// mask: false,
// icon: 'error'
// })
} else {
const errorMsg = isGuest ? res.data.message : res.data.msg;
this.$refs.uToast.show({
title: res.data.msg,
title: errorMsg || '获取失败',
type: 'default',
icon: false
})

209
utils/login.js Normal file
View File

@@ -0,0 +1,209 @@
/**
* 登录工具函数
* 抽离登录逻辑,支持静默登录和正常登录
*/
import { getsLogin } from '@/request/api/api.js';
import { bindUserApi } from '@/request/api/salesperson';
import { storeChange } from '@/request/api/api.js';
import { clearGuestMode, getRedirectInfo, clearRedirectInfo, buildUrlWithParams } from '@/common/utils/auth.js';
import { webSocketManager } from '@/utils/ws/websocket.js';
/**
* 执行登录流程
* @param {Object} options - 配置选项
* @param {boolean} options.silent - 是否静默模式
* - true: 静默登录(不显示提示,不跳转)
* - false: 正常登录(显示提示,执行跳转)
* @param {Object} options.toastRef - Toast 引用(正常登录时用于显示错误提示)
* @returns {Promise<Object>} 返回登录结果 { success: boolean, data: Object, message: string }
*/
export async function performLogin(options = { silent: false, toastRef: null }) {
const { silent = false, toastRef = null } = options;
try {
// 步骤 1显示加载提示仅非静默模式
if (!silent) {
uni.showLoading({
title: "登录中"
});
}
// 步骤 2获取微信登录凭证
const loginRes = await new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: resolve,
fail: reject
});
});
const code = loginRes.code;
// 步骤 3调用登录接口
const res = await getsLogin({
method: "post",
data: {
code: code,
status: 1
}
});
// 步骤 4隐藏加载提示仅非静默模式
if (!silent) {
uni.hideLoading();
}
// 步骤 5处理登录结果
if (res.data.errcode == 0) {
// 登录成功
// 5.1 存储用户信息
uni.setStorageSync('token', res.data.data.token);
uni.setStorageSync('user_id', res.data.data.data.user.id);
uni.setStorageSync('userinfo', res.data.data.data['user']);
// 5.2 绑定推广员(如果存在)
const salespersonId = uni.getStorageSync('salesperson_id');
if (salespersonId) {
bindUserApi({
user_id: res.data.data.data.user.id,
salesperson_id: salespersonId,
}).then((bindRes) => {
console.log(bindRes, 'bindUserApi', bindRes);
}).catch((err) => {
console.error('绑定推广员失败:', err);
});
}
// 5.3 处理门店切换/设置
const storeId = uni.getStorageSync('store_id');
if (storeId) {
storeChange({
method: "post",
data: {
store_id: storeId
}
}).catch((err) => {
console.error('切换门店失败:', err);
});
} else {
uni.setStorageSync('store_id', res.data.data.data.store_id);
}
// 5.4 清除 Guest 模式标记
clearGuestMode();
// 5.5 重新绑定 WebSocket登录成功后
try {
webSocketManager.rebindUser();
} catch (error) {
console.error('重新绑定 WebSocket 失败:', error);
}
// 5.6 页面跳转(仅非静默模式)
if (!silent) {
const redirectInfo = getRedirectInfo();
const userMobile = res.data.data.data.user.mobile;
if (userMobile != '') {
setTimeout(() => {
if (redirectInfo) {
// 有跳转信息,跳转回原页面
clearRedirectInfo();
const redirectUrl = buildUrlWithParams(redirectInfo.path, redirectInfo.params);
uni.navigateTo({
url: redirectUrl,
fail: () => {
// 如果跳转失败使用redirectTo
uni.redirectTo({
url: redirectUrl
});
}
});
} else {
// 没有跳转信息,跳转到首页
uni.reLaunch({
url: '/pages/home/home'
});
}
}, 500);
}
}
return {
success: true,
data: res.data.data,
message: '登录成功'
};
} else {
// 登录失败
const errorMsg = res.data.msg || '登录失败';
// 显示错误提示(仅非静默模式)
if (!silent && toastRef) {
toastRef.show({
title: errorMsg,
type: 'default',
icon: false
});
} else if (!silent) {
// 如果没有 toastRef使用 uni.showToast
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
});
} else {
// 静默模式:仅记录日志
console.error('静默登录失败:', errorMsg);
}
return {
success: false,
data: null,
message: errorMsg
};
}
} catch (error) {
// 处理异常
const errorMsg = error.message || '登录过程发生错误';
// 隐藏加载提示(仅非静默模式)
if (!silent) {
uni.hideLoading();
}
// 显示错误提示(仅非静默模式)
if (!silent && toastRef) {
toastRef.show({
title: errorMsg,
type: 'default',
icon: false
});
} else if (!silent) {
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
});
} else {
// 静默模式:仅记录日志
console.error('静默登录异常:', error);
}
return {
success: false,
data: null,
message: errorMsg
};
}
}
/**
* 静默登录(便捷方法)
* @returns {Promise<Object>} 返回登录结果
*/
export async function silentLogin() {
return performLogin({ silent: true });
}

View File

@@ -19,11 +19,7 @@ export class WebSocketManager {
* @returns {string|null}
*/
getToken() {
// 优先使用缓存的token
if (this.cachedToken) {
return this.cachedToken;
}
// 从本地存储获取token
// 从本地存储获取token不使用缓存确保获取最新token
let token = uni.getStorageSync('token');
if (token) {
// 移除Bearer前缀如果有
@@ -33,6 +29,37 @@ export class WebSocketManager {
return token || null;
}
/**
* 清除缓存的token登录后需要调用以获取最新token
*/
clearCachedToken() {
this.cachedToken = null;
}
/**
* 重新绑定用户(登录后调用)
*/
rebindUser() {
const userId = uni.getStorageSync('user_id');
if (!userId) {
console.warn('重新绑定用户失败user_id不存在');
return false;
}
// 清除缓存的token确保获取最新token
this.clearCachedToken();
if (this.isConnected) {
// 如果已连接,直接绑定
return this.bindUser(userId);
} else {
// 如果未连接,先连接再绑定
this.connect();
// 连接成功后会在 onOpen 中自动绑定
return true;
}
}
connect() {
if (this.isConnected) return;
@@ -56,8 +83,8 @@ export class WebSocketManager {
// 开始心跳检测
this.startHeartbeat();
// 绑定当前用户
const userId = uni.getStorageSync('userId');
// 绑定当前用户使用正确的keyuser_id
const userId = uni.getStorageSync('user_id');
if (userId) {
this.bindUser(userId);
}