Browse Source

feat(用户信息): 添加头像和昵称编辑功能

refactor(项目切换): 优化项目切换逻辑和状态管理

style(UI): 调整用户信息区域样式和布局

fix(日期选择器): 限制日期选择范围为今天之前
master
kola-web 5 days ago
parent
commit
9a7096c5ec
  1. 11
      project.private.config.json
  2. 4
      src/doctor/pages/articleList/index.scss
  3. 2
      src/doctor/pages/articleList/index.wxml
  4. 5
      src/doctor/pages/patientList/index.ts
  5. 2
      src/doctor/pages/patientList/index.wxml
  6. 4
      src/miniprogram_npm/a-calc/index.js
  7. 4
      src/miniprogram_npm/dayjs/index.js
  8. 37
      src/pages/index/index.scss
  9. 245
      src/pages/index/index.ts
  10. 23
      src/pages/index/index.wxml
  11. 7
      src/pages/tourists/index.scss
  12. 100
      src/pages/tourists/index.ts
  13. 2
      src/pages/tourists/index.wxml

11
project.private.config.json

@ -23,13 +23,20 @@
"miniprogram": { "miniprogram": {
"list": [ "list": [
{ {
"name": "协议-工作人员协议", "name": "游客-首页",
"pathName": "privacy/pages/policy/index", "pathName": "pages/tourists/index",
"query": "", "query": "",
"scene": null, "scene": null,
"launchMode": "default" "launchMode": "default"
}, },
{ {
"name": "协议-工作人员协议",
"pathName": "privacy/pages/policy/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "药店-教育详情", "name": "药店-教育详情",
"pathName": "doctor/pages/article/index", "pathName": "doctor/pages/article/index",
"query": "", "query": "",

4
src/doctor/pages/articleList/index.scss

@ -14,6 +14,8 @@ page {
top: 0; top: 0;
z-index: 1; z-index: 1;
background-color: #fff; background-color: #fff;
.scroll {
overflow-x: auto;
.tabs { .tabs {
display: flex; display: flex;
align-items: start; align-items: start;
@ -24,6 +26,7 @@ page {
line-height: 48rpx; line-height: 48rpx;
min-width: 25vw; min-width: 25vw;
box-sizing: border-box; box-sizing: border-box;
white-space: nowrap;
&.active { &.active {
position: relative; position: relative;
color: #ff8a4c; color: #ff8a4c;
@ -43,6 +46,7 @@ page {
} }
} }
} }
}
.banner { .banner {
padding: 32rpx 40rpx 0; padding: 32rpx 40rpx 0;
height: 204rpx; height: 204rpx;

2
src/doctor/pages/articleList/index.wxml

@ -4,6 +4,7 @@
<view class="page" style="padding-top: {{pageTop}}px;"> <view class="page" style="padding-top: {{pageTop}}px;">
<view class="page-header" style="top:{{pageTop}}px"> <view class="page-header" style="top:{{pageTop}}px">
<view class="scroll">
<view class="tabs"> <view class="tabs">
<view <view
class="tab {{currentCategoryId === item.id ? 'active' : ''}}" class="tab {{currentCategoryId === item.id ? 'active' : ''}}"
@ -16,6 +17,7 @@
</view> </view>
</view> </view>
</view> </view>
</view>
<swiper class="banner" wx:if="{{bannerList.length > 0}}" indicator-dots="{{false}}" autoplay circular> <swiper class="banner" wx:if="{{bannerList.length > 0}}" indicator-dots="{{false}}" autoplay circular>
<swiper-item class="swiper-item" wx:for="{{bannerList}}" wx:key="id"> <swiper-item class="swiper-item" wx:for="{{bannerList}}" wx:key="id">
<image class="s-img" src="{{item.imageUrl}}" mode="aspectFill"></image> <image class="s-img" src="{{item.imageUrl}}" mode="aspectFill"></image>

5
src/doctor/pages/patientList/index.ts

@ -34,6 +34,7 @@ Page({
jumpEndTime: '', jumpEndTime: '',
enrollStartTime: '', enrollStartTime: '',
enrollEndTime: '', enrollEndTime: '',
today: dayjs().format('YYYY-MM-DD'), // 今天日期,用于限制时间选择器
// 患者列表 // 患者列表
patientList: [] as any[], patientList: [] as any[],
@ -226,7 +227,7 @@ Page({
const index = e.detail.value const index = e.detail.value
const option = this.data.jumpStatusOptions[index] const option = this.data.jumpStatusOptions[index]
this.setData({ this.setData({
jumpStatus: option.value, jumpStatus: String(option.value),
jumpStatusIndex: index, jumpStatusIndex: index,
jumpStatusLabel: option.label, jumpStatusLabel: option.label,
page: 1, page: 1,
@ -240,7 +241,7 @@ Page({
const index = e.detail.value const index = e.detail.value
const option = this.data.enrollStatusOptions[index] const option = this.data.enrollStatusOptions[index]
this.setData({ this.setData({
enrollStatus: option.value, enrollStatus: String(option.value),
enrollStatusIndex: index, enrollStatusIndex: index,
enrollStatusLabel: option.label, enrollStatusLabel: option.label,
page: 1, page: 1,

2
src/doctor/pages/patientList/index.wxml

@ -59,6 +59,7 @@
<picker <picker
class="picker" class="picker"
mode="date" mode="date"
end="{{today}}"
bindchange="{{timeType === 0 ? 'handleJumpStartTimeChange' : 'handleEnrollStartTimeChange'}}" bindchange="{{timeType === 0 ? 'handleJumpStartTimeChange' : 'handleEnrollStartTimeChange'}}"
value="{{timeType === 0 ? jumpStartTime : enrollStartTime}}" value="{{timeType === 0 ? jumpStartTime : enrollStartTime}}"
> >
@ -68,6 +69,7 @@
<picker <picker
class="picker" class="picker"
mode="date" mode="date"
end="{{today}}"
bindchange="{{timeType === 0 ? 'handleJumpEndTimeChange' : 'handleEnrollEndTimeChange'}}" bindchange="{{timeType === 0 ? 'handleJumpEndTimeChange' : 'handleEnrollEndTimeChange'}}"
value="{{timeType === 0 ? jumpEndTime : enrollEndTime}}" value="{{timeType === 0 ? jumpEndTime : enrollEndTime}}"
> >

4
src/miniprogram_npm/a-calc/index.js

File diff suppressed because one or more lines are too long

4
src/miniprogram_npm/dayjs/index.js

File diff suppressed because one or more lines are too long

37
src/pages/index/index.scss

@ -10,6 +10,38 @@ page {
} }
.page { .page {
padding: 0 32rpx; padding: 0 32rpx;
.user-info {
padding: 40rpx 0 32rpx;
display: flex;
align-items: center;
gap: 24rpx;
.avatar-wrapper {
padding: 0;
margin: 0;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
outline: none;
background-color: transparent;
border: 1px solid #fff;
&::after {
border: none;
}
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.input {
font-size: 36rpx;
color: #ffffff;
}
.input-placeholder {
font-size: 36rpx;
color: #ffffff;
}
}
.banner { .banner {
position: relative; position: relative;
@ -154,9 +186,8 @@ page {
} }
} }
.work { .work {
position: fixed; margin-top: 52rpx;
bottom: 120rpx; padding-bottom: 100rpx;
left: 0;
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;

245
src/pages/index/index.ts

@ -29,6 +29,10 @@ Page({
selectedIndicationName: '', selectedIndicationName: '',
bannerList: [] as any[], bannerList: [] as any[],
// 用户信息
avatarUrl: '',
nickname: '',
}, },
onLoad() { onLoad() {
@ -44,34 +48,9 @@ Page({
}) })
app.waitLogin().then(() => { app.waitLogin().then(() => {
this.getProjectList()
this.getBanner() this.getBanner()
this.checkStatus() this.checkStatus()
}) this.getUserInfo()
},
// 获取项目列表
getProjectList() {
wx.ajax({
method: 'GET',
url: '/app/patient/patient/project-list',
}).then((res: any) => {
const projectList = res.list || []
const currentProjectId = res.currentProjectId
// 找到当前项目的索引
let projectIndex = 0
projectList.forEach((item: any, index: number) => {
if (item.projectId === currentProjectId) {
projectIndex = index
}
})
this.setData({
projectList,
projectIndex,
currentProjectName: projectList[projectIndex]?.projectName || '华观健康',
})
}) })
}, },
@ -117,28 +96,63 @@ Page({
checkStatus() { checkStatus() {
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '/app/patient/patient/recent-project', url: '/app/patient/patient/patient-project-list',
}) })
.then((res: any) => { .then((res: any) => {
if (res && res.projectId) { const projectList = res.list || []
// 检查扫码的项目是否与当前项目一致 const currentProjectId = res.currentProjectId
// 存储项目列表和当前项目ID
this.setData({
projectList,
currentProjectId,
})
if (projectList.length > 0) {
// 找到当前项目
const currentProject = projectList.find((p: any) => p.projectId === currentProjectId) || projectList[0]
// 检查扫码的项目是否在已绑定项目列表中
const scanProjectId = this.data.projectId const scanProjectId = this.data.projectId
if (scanProjectId && String(scanProjectId) !== String(res.projectId)) { if (scanProjectId) {
// 扫码的是不同项目,检查患者是否已参与过该项目 const scanProject = projectList.find((p: any) => String(p.projectId) === String(scanProjectId))
this.checkAndSwitchProject(scanProjectId, res) if (scanProject) {
// 扫码的项目已绑定,切换到该项目
if (String(scanProjectId) !== String(currentProjectId)) {
this.switchProject(scanProjectId)
} else { } else {
// 已有项目且与扫码项目一致,显示已参加项目状态 // 已经是当前项目,直接显示
this.setData({ this.setData({
isLogin: 1, isLogin: 1,
isPatient: 1, isPatient: 1,
projectId: res.projectId, projectId: currentProject.projectId,
projectName: res.projectName, projectName: currentProject.projectName,
currentProjectName: currentProject.projectName,
hasProject: true,
selectedIndicationName: currentProject.indicationName,
})
}
} else {
// 扫码的项目未绑定,进入选择流程(需要绑定新项目)
this.setData({
hasProject: false,
})
this.getProjectInfo()
}
} else {
// 没有扫码,显示当前项目
this.setData({
isLogin: 1,
isPatient: 1,
projectId: currentProject.projectId,
projectName: currentProject.projectName,
currentProjectName: currentProject.projectName,
hasProject: true, hasProject: true,
selectedIndicationName: res.indicationName, selectedIndicationName: currentProject.indicationName,
}) })
} }
} else { } else {
// 没有项目,获取项目列表供选择 // 没有绑定任何项目,获取项目列表供选择
this.setData({ this.setData({
hasProject: false, hasProject: false,
}) })
@ -154,60 +168,118 @@ Page({
}) })
}, },
// 检查并切换项目 // 切换项目
checkAndSwitchProject(scanProjectId: string, currentProject: any) { switchProject(projectId: string) {
// 获取患者的项目列表
wx.ajax({
method: 'GET',
url: '/app/patient/patient/project-list',
}).then((projectRes: any) => {
const projectList = projectRes.list || []
// 检查扫码的项目是否在患者已参与的项目列表中
const hasProject = projectList.some((p: any) => String(p.projectId) === String(scanProjectId))
if (hasProject) {
// 患者已参与过该项目,自动切换
wx.ajax({ wx.ajax({
method: 'POST', method: 'POST',
url: '/app/patient/patient/switch-project', url: '/app/patient/patient/switch-project',
data: { data: {
projectId: scanProjectId, projectId,
}, },
}).then(() => { })
.then(() => {
wx.showToast({ wx.showToast({
title: '已切换到该项目', title: '已切换到该项目',
icon: 'success', icon: 'success',
}) })
// 刷新页面数据 // 刷新页面数据
this.getProjectList()
this.checkStatus() this.checkStatus()
}).catch(() => { })
// 切换失败,显示当前项目 .catch(() => {
// 切换失败,重新检查状态
this.checkStatus()
})
},
// 获取用户信息
getUserInfo() {
wx.ajax({
method: 'GET',
url: '/app/patient/patient/patient-info',
}).then((res: any) => {
this.setData({ this.setData({
isLogin: 1, avatarUrl: res.avatar || '',
isPatient: 1, nickname: res.name || '',
projectId: currentProject.projectId, })
projectName: currentProject.projectName,
hasProject: true,
selectedIndicationName: currentProject.indicationName,
}) })
},
// 选择头像
onChooseAvatar(e: WechatMiniprogram.CustomEvent) {
const { avatarUrl } = e.detail
const app = getApp<IAppOption>()
// 上传头像
wx.uploadFile({
url: `${app.globalData.url }/app/common/common/upload`,
filePath: avatarUrl,
name: 'file',
header: {
Authorization: wx.getStorageSync('token'),
},
success: (res) => {
const data = JSON.parse(res.data)
if (data.code === 0) {
const url = data.data.url
this.setData({
avatarUrl: url,
}) })
// 更新用户信息(头像)
this.updateUserInfo('avatar')
} else { } else {
// 患者未参与过该项目,进入选择流程 wx.showToast({
title: data.msg || '上传失败',
icon: 'none',
})
}
},
fail: () => {
wx.showToast({
title: '上传失败',
icon: 'none',
})
},
})
},
// 昵称选择/输入时保存
onNicknameReview(e: WechatMiniprogram.CustomEvent) {
const nickname = e.detail.value
this.setData({ this.setData({
hasProject: false, nickname,
}) })
this.getProjectInfo() // 昵称修改后保存,允许空值(清空昵称)
this.updateUserInfo('name')
},
// 更新用户信息
updateUserInfo(type?: 'avatar' | 'name') {
const data: any = {}
// 如果是头像更新,只传头像
if (type === 'avatar' && this.data.avatarUrl) {
data.avatar = this.data.avatarUrl
}
// 如果是昵称更新,只传昵称
if (type === 'name') {
data.name = this.data.nickname
}
if (!data.avatar && !data.name) {
return
} }
wx.ajax({
method: 'POST',
url: '/app/patient/patient/update-info',
data,
}).then(() => {
wx.showToast({
title: '保存成功',
icon: 'success',
})
}).catch(() => { }).catch(() => {
// 获取项目列表失败,显示当前项目 wx.showToast({
this.setData({ title: '保存失败',
isLogin: 1, icon: 'none',
isPatient: 1,
projectId: currentProject.projectId,
projectName: currentProject.projectName,
hasProject: true,
selectedIndicationName: currentProject.indicationName,
}) })
}) })
}, },
@ -227,6 +299,7 @@ Page({
this.setData({ this.setData({
projectId: res.projectId, projectId: res.projectId,
projectName: res.projectName, projectName: res.projectName,
currentProjectName: res.projectName,
options: res.indications.map((item: any) => ({ options: res.indications.map((item: any) => ({
indicationId: item.indicationId, indicationId: item.indicationId,
indicationName: item.indicationName, indicationName: item.indicationName,
@ -296,6 +369,9 @@ Page({
params.projectId = this.data.projectId params.projectId = this.data.projectId
} }
// 添加适应症ID
params.indicationId = selectedOption.indicationId
wx.ajax({ wx.ajax({
method: 'POST', method: 'POST',
url: '/app/patient/patient/wx-login', url: '/app/patient/patient/wx-login',
@ -303,34 +379,17 @@ Page({
}).then((res: any) => { }).then((res: any) => {
const jumpUrl = res.jumpUrl const jumpUrl = res.jumpUrl
// 如果有药师ID,创建绑定关系 // 直接跳转药箱小程序
if (this.data.pharmacistId) {
wx.ajax({
method: 'POST',
url: '/app/patient/patient/bind-pharmacist',
data: {
pharmacistId: Number.parseInt(this.data.pharmacistId),
projectId: this.data.projectId,
indicationId: selectedOption.indicationId,
},
}).then(() => {
// 绑定成功后跳转
wx.navigateToMiniProgram({
appId: 'wx05551c5ee1fd1c12',
path: jumpUrl,
})
this.checkStatus()
})
} else {
// 没有药师ID,直接跳转
wx.navigateToMiniProgram({ wx.navigateToMiniProgram({
appId: 'wx05551c5ee1fd1c12', appId: 'wx05551c5ee1fd1c12',
path: jumpUrl, path: jumpUrl,
success: () => { success: () => {
this.checkStatus() this.checkStatus()
}, },
fail: () => {
this.checkStatus()
},
}) })
}
}) })
} }
}, },

23
src/pages/index/index.wxml

@ -1,17 +1,36 @@
<page-meta page-style="{{ popupShow ? 'overflow: hidden;' : '' }}" /> <page-meta page-style="{{ popupShow ? 'overflow: hidden;' : '' }}" />
<navbar fixed custom-style="background:transparent" back> <navbar fixed custom-style="background:transparent" back>
<picker wx:if="{{projectList.length > 0}}" class="page-switch" slot="left" mode="selector" range="{{projectList}}" range-key="projectName" value="{{projectIndex}}" bindchange="onProjectChange"> <picker
wx:if="{{projectList.length > 0}}"
class="page-switch"
slot="left"
mode="selector"
range="{{projectList}}"
range-key="projectName"
value="{{projectIndex}}"
bindchange="onProjectChange"
>
{{currentProjectName}} {{currentProjectName}}
<text style="font-size: 0.5em; vertical-align: super">®</text> <text style="font-size: 0.5em; vertical-align: super">®</text>
<van-icon class="arrow" name="arrow-down" /> <van-icon class="arrow" name="arrow-down" />
</picker> </picker>
<view wx:else class="page-title">华观健康</view> <view wx:elif="{{currentProjectName}}" class="page-switch" slot="left">
{{currentProjectName}}
<text style="font-size: 0.5em; vertical-align: super">®</text>
</view>
</navbar> </navbar>
<view <view
class="page" class="page"
style="background: url('{{imageUrl}}bg2.png?t={{Timestamp}}') no-repeat top center/100% 558rpx;padding-top: {{pageTop+24}}px;" style="background: url('{{imageUrl}}bg2.png?t={{Timestamp}}') no-repeat top center/100% 558rpx;padding-top: {{pageTop+24}}px;"
> >
<!-- 用户信息 -->
<view class="user-info" wx:if="{{isLogin === 1}}">
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl || imageUrl + 'default-avatar.png'}}"></image>
</button>
<input type="nickname" class="input" placeholder-class="input-placeholder" placeholder="我的昵称" value="{{nickname}}" bindnicknamereview="onNicknameReview" />
</view>
<swiper class="banner" wx:if="{{bannerList.length > 0}}"> <swiper class="banner" wx:if="{{bannerList.length > 0}}">
<swiper-item class="b-item"> <swiper-item class="b-item">
<image class="b-img" wx:for="{{bannerList}}" wx:key="id" src="{{item.imageUrl}}"></image> <image class="b-img" wx:for="{{bannerList}}" wx:key="id" src="{{item.imageUrl}}"></image>

7
src/pages/tourists/index.scss

@ -16,11 +16,12 @@ page {
align-items: center; align-items: center;
gap: 24rpx; gap: 24rpx;
.avatar-wrapper { .avatar-wrapper {
padding: 0;
margin: 0; margin: 0;
width: 104rpx; width: 104rpx;
height: 104rpx; height: 104rpx;
border-radius: 50%; border-radius: 50%;
border: none; border: 1px solid #fff;
outline: none; outline: none;
background-color: transparent; background-color: transparent;
&::after { &::after {
@ -34,11 +35,11 @@ page {
} }
.input { .input {
font-size: 36rpx; font-size: 36rpx;
color: #ffffff; color: #121111;
} }
.input-placeholder { .input-placeholder {
font-size: 36rpx; font-size: 36rpx;
color: #ffffff; color: #121111;
} }
} }
.page-banner { .page-banner {

100
src/pages/tourists/index.ts

@ -1,9 +1,105 @@
const app = getApp<IAppOption>() const app = getApp<IAppOption>()
Page({ Page({
data: {}, data: {
avatarUrl: '',
nickname: '',
},
onLoad() { onLoad() {
app.waitLogin().then(() => {}) app.waitLogin().then(() => {
this.getUserInfo()
})
},
// 获取用户信息
getUserInfo() {
wx.ajax({
method: 'GET',
url: '/app/patient/patient/patient-info',
}).then((res: any) => {
this.setData({
avatarUrl: res.avatar || '',
nickname: res.name || '',
})
})
},
// 选择头像
onChooseAvatar(e: WechatMiniprogram.CustomEvent) {
const { avatarUrl } = e.detail
const app = getApp<IAppOption>()
// 上传头像
wx.uploadFile({
url: `${app.globalData.url}/app/common/common/upload`,
filePath: avatarUrl,
name: 'file',
header: {
Authorization: wx.getStorageSync('token'),
},
success: (res) => {
const data = JSON.parse(res.data)
if (data.code === 0) {
const url = data.data.url
this.setData({
avatarUrl: url,
})
// 更新用户信息(头像)
this.updateUserInfo('avatar')
} else {
wx.showToast({
title: data.msg || '上传失败',
icon: 'none',
})
}
},
fail: () => {
wx.showToast({
title: '上传失败',
icon: 'none',
})
},
})
},
// 昵称选择/输入时保存
onNicknameReview(e: WechatMiniprogram.CustomEvent) {
const nickname = e.detail.value
this.setData({
nickname,
})
// 昵称修改后保存,允许空值(清空昵称)
this.updateUserInfo('name')
},
// 更新用户信息
updateUserInfo(type?: 'avatar' | 'name') {
const data: any = {}
// 如果是头像更新,只传头像
if (type === 'avatar' && this.data.avatarUrl) {
data.avatar = this.data.avatarUrl
}
// 如果是昵称更新,只传昵称
if (type === 'name') {
data.name = this.data.nickname
}
if (!data.avatar && !data.name) {
return
}
wx.ajax({
method: 'POST',
url: '/app/patient/patient/update-info',
data,
})
.then(() => {
wx.showToast({
title: '保存成功',
icon: 'success',
})
})
.catch(() => {
wx.showToast({
title: '保存失败',
icon: 'none',
})
})
}, },
// 跳转到药店工作人员登录页 // 跳转到药店工作人员登录页
goToPharmacist() { goToPharmacist() {

2
src/pages/tourists/index.wxml

@ -10,7 +10,7 @@
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar"> <button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl}}"></image> <image class="avatar" src="{{avatarUrl}}"></image>
</button> </button>
<input type="nickname" class="input" placeholder-class="input-placeholder" placeholder="我的昵称" /> <input type="nickname" class="input" placeholder-class="input-placeholder" placeholder="我的昵称" value="{{nickname}}" bindnicknamereview="onNicknameReview" />
</view> </view>
<image class="page-banner" src="{{imageUrl}}start1.png?t={{Timestamp}}"></image> <image class="page-banner" src="{{imageUrl}}start1.png?t={{Timestamp}}"></image>
<view class="page-options"> <view class="page-options">

Loading…
Cancel
Save