Compare commits

...
29 Commits
Author SHA1 Message Date
zhanglei b06a08b029 feat(home): 首页智慧体重/健康档案入口接入H5并切换生产环境
- 智慧体重、健康档案及AI随访banner由占位页改为跳转对应H5页面,
  携带加密参数并增加登录校验
- UrlConfig新增体重管理、健康档案H5地址,baseUrlType切换为PRODUCT
- 版本号升级至1.0.15(1015)
2026-09-04 11:35:40 +08:00
zhanglei 3c90d9f1d3 refactor(emergency): 重构应急就医群聊入口并切换测试环境
- EmergencyFragment: 抽取 startParamedicChat() 公共方法,用 when 表达式
  替代嵌套 if-else,消除单工单与多工单选择两路径的重复代码
- IMInputActionSettingUtils: createEmergencySetting 新增 firstAid 参数,
  支持区分急救员/非急救员的 IM 输入态配置
- UrlConfig: 切换测试环境 API 与 H5 地址(xjxc-api → xj-api.yixiong-tech.com,
  H5 → 10.10.10.228:23006),baseUrlType 由 PRODUCT 切回 TEST 以便联调
2026-08-04 20:03:28 +08:00
zhanglei 5a2963ce32 refactor(web): 优化WebView配置和页面刷新逻辑
- 移除重复的RecyclerView相关导入语句
- 使用SPUtils和CHAT_IS_LIAISON_USER常量替代原有导入
- 将ToastUtil替换为showToast方法
- 设置WebView缓存模式为不缓存
- 在返回事件处理中添加stopLoading调用
- 移除motionClock页面的刷新标志位和相关逻辑
2026-08-04 17:25:46 +08:00
zhangxiao 7a591579a8 feat(chat): 添加联络员用户标识功能
- 引入 CHAT_IS_LIAISON_USER 常量用于标识联络员用户
- 在 EmergencyFragment 中存储联络员状态到 SharedPreferences
- 在 TUIBaseChatActivity 中读取联络员标识并控制布局显示逻辑
- 添加 SPUtils 工具类用于本地数据持久化
- 仅对联络员用户显示带导航栏的聊天界面布局
2026-07-27 18:48:55 +08:00
zhangxiao 41fadb7b75 feat(chat): 添加群成员职位显示和更新功能
- 在 AddMemberActivity 中为 ExpertBean 和 EmployeeBean 添加职位获取方法
- 扩展 AddMemberAdapter 以显示成员职位信息
- 添加 ExpertBean 的 post_dictText 字段用于职位文本显示
- 更新群组聊天界面布局以显示成员职位
- 添加更新群成员按钮和相关功能实现
- 优化群组提示监听器的时序问题处理
- 修改提案页面布局中的占位文本为实际数据绑定
- 添加应急工单对话框的新样式和相关资源文件
2026-07-22 17:14:44 +08:00
zhangxiao c504f23052 feat(chat): 优化群聊功能和界面组件
- 添加群成员头像基地址配置和统一管理
- 重构群成员列表布局和样式调整
- 添加群成员加入提示消息监听和处理机制
- 优化群聊头像加载和默认图标显示
- 重构群聊页面导航和界面元素
- 添加急救订单选择对话框功能
- 优化通话和定位分享功能实现
- 新增文件基地址辅助类和接口定义
- 重构群聊输入组件和布局配置
2026-07-21 18:49:19 +08:00
zhangxiao aee280cf86 refactor(tuichat): 重构添加成员页面UI和交互逻辑
- 将顶部标题栏改为白底黑字风格,调整返回按钮颜色
- 移除原有的Tab标签页,改为搜索框配合分段Tab布局
- 添加搜索框右侧"搜索"文字按钮功能
- 重新设计底部确认按钮为全宽度样式
- 调整导航面板宽度从220dp增加到280dp
- 新增多个颜色值和图形资源用于支持新UI样式
- 更新通话图标和分隔线样式
- 修改应急救援相关API接口和数据模型
- 调整患者信息展示逻辑和界面元素可见性控制
2026-07-21 08:56:38 +08:00
zhangxiao c044b8cbdc feat(chat): 添加群组成员管理功能
- 新增 AddMemberActivity 页面实现选择联系人界面
- 实现双标签页切换(邀请专家/邀请员工)功能
- 添加搜索框支持实时过滤联系人列表
- 集成 RecyclerView 列表展示与多选功能
- 实现底部确认按钮回传选中用户ID列表
- 添加 GroupMemberNavAdapter 群成员导航适配器
- 扩展 GroupMemberInfo 类添加业务字段支持
- 更新聊天导航头部布局优化界面结构
- 新增电话拨打对话框组件支持一键拨号
- 完善群组信息成员详情泛型定义增强类型安全
2026-07-17 17:59:55 +08:00
zhangxiao 14f7542037 feat(chat): 添加群组管理功能和微信分享支持
- 新增 EmployeeBean、ExpertBean 和 GroupMemberBean 数据模型
- 实现群组成员管理接口和操作辅助类
- 添加微信 SDK 依赖并集成位置信息分享功能
- 在聊天界面中实现群成员列表显示和动态更新
- 增加急救员群组创建和管理的相关 API 接口
- 实现群组成员搜索、添加和移除功能
- 添加位置消息长按分享到微信的功能
2026-07-16 18:47:03 +08:00
zhangxiao 7cca7e0b79 feat(chat): 添加群聊右侧导航抽屉和健康档案功能
- 集成 androidx.drawerlayout:drawerlayout:1.2.0 依赖
- 新增 chat_activity_with_navigation.xml 布局文件实现抽屉布局
- 新增 chat_navigation_header.xml 头部布局显示群成员信息
- 在 TUIBaseChatActivity 中实现抽屉打开关闭功能
- 修改 InputView 中的群视频通话逻辑,针对特定包名使用新方案
- 添加 ProposalBean 数据模型用于健康档案信息
- 实现 ProposalActivity 页面展示患者健康档案详情
- 添加 EmergencyApi 接口获取健康档案数据
- 在 HomeFragment 中增加登录验证跳转健康档案页面
- 完善群聊界面右滑抽屉显示群成员列表功能
2026-07-15 17:50:27 +08:00
zhangxiao b29c46b906 feat(chat): 为健康客户端添加位置消息长按功能和应急就医相关功能
- 在LocationMessageHolder中为健康客户端添加位置消息长按监听器
- 优化TUIGroupChatFragment中的代码格式和空格调整
- 在TUIGroupChatFragment中添加对健康客户端的特殊处理逻辑
- 注册ProposalActivity到AndroidManifest.xml
- 添加健康档案页面相关的颜色定义
- 在EmergencyFragment中添加急救人员相关功能和界面元素
- 更新HomeFragment中的导航逻辑以跳转到健康档案页面
- 创建ProposalActivity和ProposalViewModel用于健康档案功能
- 添加应急就医群的相关字符串资源
2026-07-14 19:15:11 +08:00
zhangleiandClaude Opus 4.7 4b5da9269d feat(home): 健康资讯模块增加登录校验及未登录态UI处理
所有健康资讯H5跳转入口(文章详情、健康知识、健康咨询、搜索)增加
登录状态校验,未登录时提示"请先登录"并引导至登录页;健康知识模块
未登录时隐藏Tab栏显示"暂未登录"空态。版本号升至 1.0.14。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 18:22:04 +08:00
zhangleiandClaude Opus 4.7 7a830c3272 fix(home): 修复搜索框自动弹起键盘及H5搜索参数名错误
onResume 时清除搜索框焦点避免页面返回后键盘自动弹出,清除方法
封装为公共方法避免外部直接访问内部 View;修正 H5 搜索页参数名
searchVal → secrchVal 对齐服务端约定。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 15:56:07 +08:00
zhangleiandClaude Opus 4.7 b609f07240 fix(home): 修复健康知识列表项标签过长时与日期重叠
标签文本新增单行省略号处理及右边距限制,移除图片固定居左居中对齐属性,
避免标签文字过长时撑破布局或与右侧日期重叠。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 09:40:49 +08:00
zhangleiandClaude Opus 4.7 c33c76f732 fix(home): 修复健康资讯搜索功能及咨询栏目加载逻辑
搜索框从 TextView 改为 EditText 以支持键盘输入和搜索键触发,
搜索回调携带关键字参数透传到 H5 搜索页。健康咨询栏目从硬编码
categoryId=2 改为从栏目列表动态读取第二个栏目 ID。新增新疆项目
H5 基础地址方法修正生产环境 URL,健康知识 H5 路径回归使用
getHealthNewsH5BaseUrl 避免丢失 healthnewsweb 路径层级。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 09:22:07 +08:00
zhangleiandClaude Opus 4.7 bb23c00e9c fix(home): 修正搜索栏布局及Tab样式细节
搜索框从EditText改为TextView入口,搜索按钮改为主题色圆角按钮,
Tab文字颜色统一引用子Tab专用选择器,drawable颜色引用修正为主题色。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-29 16:48:26 +08:00
zhangleiandClaude Opus 4.7 f5be575491 feat(home): 新增健康资讯模块(健康知识 + 健康咨询)
采用 HMAC-SHA256 签名认证获取 Token,通过栏目/文章 API 获取数据,
详情/更多/搜索均跳转 H5 页面。同时新增带下划线指示器的 Tab 样式。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-29 16:47:46 +08:00
zhanglei 4ff52c0465 feat(web): 支持WebActivity横竖屏切换不重建及多项功能调整
- WebActivity 改为 configChanges 处理横竖屏,不再重走生命周期,并启用 onConfigurationChanged 切换状态栏样式
- WatchBindDialog 已绑定状态下禁用确认按钮并降低透明度,隐藏解绑功能
- HomeFragment 注释掉解绑设备调用逻辑
- UrlConfig 更新测试环境地址及健康体检 H5 地址
- activity_archives_detail.xml 修复 include 布局缺少宽高属性问题
- 版本升级至 1.0.13(versionCode 1013)
2026-04-17 11:23:41 +08:00
zhanglei f4578f94b1 修改静态地图的key 2026-04-08 14:27:15 +08:00
zhanglei 9aa64d5996 feat(home): 新增健康体检H5跳转功能并升级版本至1.0.12
- HomeFragment 中 Tab2 和 Banner1 点击事件由跳转 EmptyActivity 改为
  打开健康体检 H5 页面,增加登录状态前置校验
- UrlConfig 新增健康体检 H5 地址常量 PRODUCT_HOME_HEALTH_CHECK_H5
  及 getHealthCheckH5Url() 方法
- 切换应用运行环境为 PRODUCT,middle 路径由 xj_health/ 改为 hb/
- 版本号升级:1.0.11 (1011) → 1.0.12 (1012)
2026-03-27 14:15:53 +08:00
zhanglei a4bce0ff23 refactor(watch): 重构手表绑定对话框及首页卡片布局
- 移除首页手表卡片的 Tab 分类功能(心脑血管/癌症等)
- 重构绑定对话框:更新标题为健康监测工具绑定、简化用户信息展示、优化 SN 编码输入框
- 简化未绑定手表占位视图,仅保留绑定按钮
- 切换环境配置至测试环境(TEST/xj_health/)
2026-03-19 17:04:49 +08:00
zhanglei e76474b74e feat(home): 新增银行积分 API 接口和配置
- 新增 HomeBankBean 数据模型,包含今日积分和总积分字段
- 在 HomeApi 中新增 postUserPointDetail() 接口,支持自定义请求头和签名
- 新增银行积分 Retrofit 配置:getBankRetrofit() 函数
- 新增银行积分 BASE_URL 配置和 SECRET_KEY 密钥
- 优化 UrlConfig,支持测试和生产环境的银行积分 API 地址切换
2026-03-17 17:02:22 +08:00
zhanglei 72f4c21b3a feat(home): 新增银行积分签名工具和网络请求封装
- 新增 SignatureUtils 工具类,实现 HMAC-SHA256 签名算法
- 新增 BankPointRequest 网络请求封装类,简化 API 调用逻辑
- 优化 HomeFragment 中的 getBankPointDetail() 方法,从 34 行简化为 20 行
- 优化 AppraiseDialog,移除过时的版本判断和冗余的初始化逻辑
- 补充完整的中文文档注释和单元测试
2026-03-17 16:56:39 +08:00
zhanglei 222c1e4570 对话框逻辑修改 2026-03-17 09:20:34 +08:00
zhanglei 06352cbf11 feat(watch): 新增手表绑定/解绑功能
- 新增 WatchBindDialog 对话框,支持手表序列号输入和绑定/解绑操作
- HomeApi 新增 getBindDevice 和 getUnbundleDevice 接口
- HomeRepository 新增对应的绑定/解绑方法
- HomeViewModel 新增 watchBindStatus 状态流,用于监听绑定结果
- HomeFragment 新增手表绑定对话框交互逻辑
- 优化手表头部点击事件,已绑定时跳转详情,未绑定时显示绑定对话框
- 完善手表信息展示和状态管理
2026-03-16 09:29:21 +08:00
zhanglei d0a92cea60 feat(watch): 新增已登录未绑定手表的图文占位视图
- 新增 layout_no_watch 占位视图,包含图片、提示文字和"去绑定"按钮
- 新增 onBindWatchClick 回调,供外部处理绑定跳转逻辑
- 提取 showLoginEmpty() 和 showNoWatchPlaceholder() 辅助方法,统一状态管理
- 优化 setWatchInfo() 逻辑,完善已登录无手表时的视图切换
- 修复 init() 中 Tab 切换时的占位视图显示逻辑
- 改进代码格式和缩进规范
2026-03-16 09:25:33 +08:00
zhanglei c6b5966261 fix(watch): 修复手表模块未登录及无设备时的显示逻辑
- 新增 hasWatch 标志位区分用户是否绑定手表设备
- 未登录时隐藏轮播视图并显示"暂未登录"提示
- 已登录但无手表时显示"您目前未配备智能穿戴设备"提示
- 仅在已登录且有手表设备时才展示数据轮播
2026-03-12 10:53:39 +08:00
zhanglei 008408470a refactor(home): 重构首页健康银行入口及 URL 环境配置
- 移除健康银行 H5 入口的环境限制,统一适配测试与生产环境
- 新增 getBankH5Url() 方法,按环境分别返回对应 H5 地址
- 将 selectDoctorRecommendHome() 移至登录状态判断内,避免未登录时发起无效请求
- 重构首页头部布局,改为双图片 + 渐变背景的组合方式
- 新增 home_head_img1/2 图片资源
- 删除废弃的 UrlH5RouteUtils 工具类
- 清理 UrlConfig 中大量废弃注释代码
2026-03-11 17:50:44 +08:00
zhangleiandClaude Sonnet 4.6 967aff4103 refactor(web): 优化 WebActivity 状态栏控制逻辑并清理无用参数
- 新增 hideStatus 参数,支持健康银行H5以全屏无状态栏模式打开
- 移除 startWebActivity 的 source 绿色主题参数及相关逻辑
- 注释 WebActivity.onConfigurationChanged 横竖屏状态栏切换逻辑
- 移除 AndroidInterface.finishActivity() 中多余的 WebReloadEvent 发送
- 健康银行H5入口改为 isFull=true、hideStatus=true 模式打开

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:02:40 +08:00
105 changed files with 5936 additions and 411 deletions
+1 -1
View File
@@ -137,5 +137,5 @@ dependencies {
//JSoup
api 'org.jsoup:jsoup:1.12.1'
implementation 'com.tencent.mm.opensdk:wechat-sdk-android:+'
}
+3 -3
View File
@@ -102,7 +102,7 @@
android:screenOrientation="portrait" />
<activity
android:name=".ui.activity.WebActivity"
android:screenOrientation="portrait" />
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity
android:name=".ui.activity.UserInfoSettingActivity"
android:screenOrientation="portrait" />
@@ -199,7 +199,7 @@
android:screenOrientation="portrait"/>
<activity android:name=".ui.activity.EmptyActivity"
android:screenOrientation="portrait"/>
<activity android:name=".ui.activity.ProposalActivity"
android:screenOrientation="portrait"/>
</application>
</manifest>
@@ -6,6 +6,7 @@ import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Message
import android.util.Log
import android.view.Gravity
import android.view.KeyEvent
import android.view.View
@@ -19,9 +20,18 @@ import com.permissionx.guolindev.PermissionX
import com.sw.healthyclients.data.local.DataStoreManager
import com.sw.healthyclients.utils.CustomActivityManager
import com.sw.healthyclients.view.CustomToast
import com.tencent.mm.opensdk.modelmsg.SendMessageToWX
import com.tencent.mm.opensdk.modelmsg.WXMediaMessage
import com.tencent.mm.opensdk.modelmsg.WXWebpageObject
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.tencent.qcloud.tuicore.TUICore
import com.tencent.qcloud.tuikit.tuichat.TUIChatService
import com.tencent.qcloud.tuikit.tuichat.classicui.event.IMChatEvent
import com.tencent.qcloud.tuikit.tuichat.interfaces.FileBaseUrlListener
import com.tencent.qcloud.tuikit.tuichat.interfaces.ShareToWeChatListener
import com.tencent.qcloud.tuikit.tuichat.presenter.FileBaseUrlHelper
import com.tencent.qcloud.tuikit.tuichat.presenter.WeChatShareHelper
import com.xjjk.healthyclients.base.BaseVMBActivity
import com.xjjk.healthyclients.bean.CardInfoBean
import com.xjjk.healthyclients.bottomtab.HomeBottomTabLayout
@@ -32,6 +42,7 @@ import com.xjjk.healthyclients.fragment.EmergencyFragment
import com.xjjk.healthyclients.fragment.GuidanceFragment
import com.xjjk.healthyclients.fragment.HomeFragment
import com.xjjk.healthyclients.fragment.MonitorFragment
import com.xjjk.healthyclients.retrofit.UrlConfig
import com.xjjk.healthyclients.superfuntion.loginIm
import com.xjjk.healthyclients.superfuntion.startAllDoctorActivity
import com.xjjk.healthyclients.superfuntion.startLoginActivity
@@ -68,6 +79,42 @@ class MainActivity : BaseVMBActivity<MainViewModel, ActivityMainBinding>(R.layou
Manifest.permission.POST_NOTIFICATIONS
)
val APP_ID: String = "wxb33cd60beda0a212"
private var api: IWXAPI? = null
private val shareToWeChatListener = ShareToWeChatListener { lat, lon, desc ->
val url = ("https://ditu.amap.com/regeo?lng=" + lon
+ "&lat=" + lat + "&name=" + desc + "&src=uriapi&innersrc=uriapi")
Log.i("TAGG", url)
val webpage = WXWebpageObject()
webpage.webpageUrl = url
val msg = WXMediaMessage(webpage)
msg.title = desc
msg.description = desc
val req = SendMessageToWX.Req()
req.transaction = System.currentTimeMillis().toString()
req.message = msg
req.scene = SendMessageToWX.Req.WXSceneSession
api?.sendReq(req)
}
private val fileBaseUrlListener = FileBaseUrlListener { UrlConfig.IMAGE_BASE_URL }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
regToWx()
WeChatShareHelper.getInstance().setListener(shareToWeChatListener)
FileBaseUrlHelper.getInstance().setListener(fileBaseUrlListener)
}
private fun regToWx() {
// 通过 WXAPIFactory 工厂,获取 IWXAPI 的实例
api = WXAPIFactory.createWXAPI(this, APP_ID, false);
// 将应用的 appId 注册到微信
api?.registerApp(APP_ID);
}
override fun initView(savedInstanceState: Bundle?) {
UpdateBuilder.create().check()// 启动更新任务
@@ -81,9 +128,9 @@ class MainActivity : BaseVMBActivity<MainViewModel, ActivityMainBinding>(R.layou
// main_tab_layout.setUnreadTip(TAG_INDEX, "99+")
// main_tab_layout.setUnreadTip(TAG_COLLECT, null, false)
}
}
override fun initData() {
// if(DataStoreManager.isLogin()){
// getAppViewModel().getIMSig(successCall = {
@@ -72,7 +72,8 @@ class MyApplication : Application(), ViewModelStoreOwner {
Log.i(TAG, "onCreate")
super.onCreate()
ConstantUtils.mIsCloseDialog=true
LocationMessageHolder.mapKey = "a87b89a2b4a32ed31da04d0996035cc1"
// LocationMessageHolder.mapKey = "a87b89a2b4a32ed31da04d0996035cc1"
LocationMessageHolder.mapKey = "e9926badd26aaa766d13439c88a83f2d"
Logger.addLogAdapter(AndroidLogAdapter())
appContext = this
DownloadFileUtil.init(this)
@@ -0,0 +1,27 @@
package com.xjjk.healthyclients.adapter
import android.widget.ImageView
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.bean.home.ArticleBean
import com.xjjk.healthyclients.superfuntion.loadRoundedImage
import com.xjjk.healthyclients.superfuntion.orEmptyDefault
/**
* 健康咨询列表 Adapter
*/
class HealthConsultationAdapter :
BaseQuickAdapter<ArticleBean, BaseViewHolder>(R.layout.item_health_consultation) {
override fun convert(holder: BaseViewHolder, item: ArticleBean) {
// 标题
holder.setText(R.id.tv_title, item.articleTitle.orEmptyDefault())
// 收藏和分享数
holder.setText(R.id.tv_meta, "收藏 ${item.favoriteCount} 分享 ${item.likeCount}")
// 封面图片 80x60 圆角8
holder.getView<ImageView>(R.id.iv_cover)?.loadRoundedImage(
item.articleHeaderimage, 8f,R.mipmap.ic_placeholder_rectangle
)
}
}
@@ -0,0 +1,32 @@
package com.xjjk.healthyclients.adapter
import android.widget.ImageView
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.sw.healthyclients.utils.DateUtil
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.bean.home.ArticleBean
import com.xjjk.healthyclients.superfuntion.loadRoundedImage
import com.xjjk.healthyclients.superfuntion.orEmptyDefault
/**
* 健康知识列表 Adapter
*/
class HealthKnowledgeAdapter :
BaseQuickAdapter<ArticleBean, BaseViewHolder>(R.layout.item_health_knowledge) {
override fun convert(holder: BaseViewHolder, item: ArticleBean) {
// 封面图片 100x100 圆角15
holder.getView<ImageView>(R.id.iv_cover)?.loadRoundedImage(
item.articleHeaderimage, 15f, R.mipmap.ic_placeholder_square,
)
// 标题
holder.setText(R.id.tv_title, item.articleTitle.orEmptyDefault())
// 摘要
holder.setText(R.id.tv_detail, item.articleSubtitle.orEmptyDefault(""))
// 标签文本
holder.setText(R.id.tv_tag, item.articleTag.orEmptyDefault())
// 日期
holder.setText(R.id.tv_date, DateUtil.long2ShortDateStr(item.createTime))
}
}
@@ -0,0 +1,93 @@
package com.xjjk.healthyclients.bankRequest
import com.xjjk.healthyclients.bean.home.HomeBankBean
import com.xjjk.healthyclients.data.api.HomeApi
import com.xjjk.healthyclients.data.bean.ApiResponse
import com.xjjk.healthyclients.retrofit.UrlConfig
import com.xjjk.healthyclients.superfuntion.toJson
import com.xjjk.healthyclients.utils.SignatureUtils
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
/**
* 银行积分网络请求封装类
* 负责处理签名生成、请求头构造、API 调用等逻辑
*/
class BankPointRequest(private val api: HomeApi) {
/**
* 获取银行积分详情
*
* @param userId 用户 ID
* @param onSuccess 成功回调
* @param onFailure 失败回调
*/
fun getBankPointDetail(
userId: String,
onSuccess: (HomeBankBean?) -> Unit,
onFailure: (Throwable) -> Unit
) {
try {
// 生成时间戳(秒级)
val timestamp = System.currentTimeMillis() / 1000
// 构造请求体
val bodyString = """{"userId":"$userId"}"""
// 生成签名
val sign = SignatureUtils.generateSignature(
timestamp,
bodyString,
UrlConfig.SECRET_KEY
)
// 构造请求头
val headers = buildHeaders(timestamp, sign)
// 构造请求体 Map
val body = mapOf("userId" to userId)
// 发送请求
api.postUserPointDetail(headers, body.toJson().toRequestBody())
.enqueue(object : Callback<ApiResponse<HomeBankBean>> {
override fun onResponse(
call: Call<ApiResponse<HomeBankBean>>,
response: Response<ApiResponse<HomeBankBean>>
) {
if (response.code()==200) {
val data = response.body()?.data
onSuccess(data)
} else {
val error = Exception("HTTP ${response.code()}: ${response.message()}")
onFailure(error)
}
}
override fun onFailure(call: Call<ApiResponse<HomeBankBean>>, t: Throwable) {
onFailure(t)
}
})
} catch (e: Exception) {
onFailure(e)
}
}
/**
* 构造请求头
*
* @param timestamp 时间戳
* @param sign 签名
* @return 请求头 Map
*/
private fun buildHeaders(timestamp: Long, sign: String): Map<String, String> {
return mapOf(
"Content-Type" to "application/json; charset=utf-8",
"X-Timestamp" to timestamp.toString(),
"X-Sign" to sign
)
}
}
@@ -0,0 +1,182 @@
package com.xjjk.healthyclients.bankRequest
import com.sw.healthyclients.data.local.DataStoreManager
import com.xjjk.healthyclients.bean.home.ArticleBean
import com.xjjk.healthyclients.bean.home.ArticleListBean
import com.xjjk.healthyclients.bean.home.CategoryBean
import com.xjjk.healthyclients.data.api.CategoryListResponse
import com.xjjk.healthyclients.data.api.HealthInfoApi
import com.xjjk.healthyclients.data.api.TokenResponse
import com.xjjk.healthyclients.retrofit.UrlConfig
import com.xjjk.healthyclients.retrofit.getBankRetrofit
import com.xjjk.healthyclients.superfuntion.toJson
import com.xjjk.healthyclients.utils.SignatureUtils
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
/**
* 健康资讯网络请求封装类
* 负责 Token 获取(签名认证)、栏目列表、文章列表的 API 调用
*/
class HealthInfoRequest {
private val api: HealthInfoApi = getBankRetrofit()
.create(HealthInfoApi::class.java)
companion object {
/** 缓存的 Token,避免重复请求 */
private var cachedToken: String? = null
}
/**
* 获取 Token(带 HMAC-SHA256 签名)
*/
fun fetchToken(
onSuccess: (String) -> Unit,
onFailure: (Throwable) -> Unit
) {
// 如果已有缓存 Token,直接返回
cachedToken?.let {
onSuccess(it)
return
}
try {
val timestamp = System.currentTimeMillis() / 1000
val userId = DataStoreManager.getUserId() ?: ""
val workNo = DataStoreManager.getUserInfo()?.workNo ?: ""
val bodyString = """{"userId":"$userId","workNo":"$workNo"}"""
val sign = SignatureUtils.generateSignature(timestamp, bodyString, UrlConfig.SECRET_KEY)
val headers = mapOf(
"Content-Type" to "application/json; charset=utf-8",
"X-Timestamp" to timestamp.toString(),
"X-Sign" to sign
)
val body = mapOf("userId" to userId, "workNo" to workNo)
api.getToken(headers, body.toJson().toRequestBody())
.enqueue(object : Callback<TokenResponse> {
override fun onResponse(
call: Call<TokenResponse>,
response: Response<TokenResponse>
) {
if (response.code() == 200) {
val token = response.body()?.data?.token
if (!token.isNullOrEmpty()) {
cachedToken = token
onSuccess(token)
} else {
onFailure(Exception("Token 为空"))
}
} else {
onFailure(Exception("HTTP ${response.code()}: ${response.message()}"))
}
}
override fun onFailure(
call: Call<TokenResponse>,
t: Throwable
) {
onFailure(t)
}
})
} catch (e: Exception) {
onFailure(e)
}
}
/**
* 获取栏目列表
*/
fun fetchCategoryList(
token: String,
onSuccess: (MutableList<CategoryBean>) -> Unit,
onFailure: (Throwable) -> Unit
) {
try {
val headers = mapOf("token" to token)
api.getCategoryList(headers)
.enqueue(object : Callback<CategoryListResponse> {
override fun onResponse(
call: Call<CategoryListResponse>,
response: Response<CategoryListResponse>
) {
if (response.code() == 200) {
val data = response.body()?.data ?: mutableListOf()
onSuccess(data)
} else {
onFailure(Exception("HTTP ${response.code()}: ${response.message()}"))
}
}
override fun onFailure(
call: Call<CategoryListResponse>,
t: Throwable
) {
onFailure(t)
}
})
} catch (e: Exception) {
onFailure(e)
}
}
/**
* 分页查询文章列表
*
* @param token 认证 Token
* @param categoryId 栏目 ID
* @param pageNum 页码
* @param pageSize 每页数量
* @param articleType 文章类型,"0"-图文,"1"-视频,不传则全部
*/
fun fetchArticleList(
token: String,
categoryId: Int,
pageNum: Int = 1,
pageSize: Int = 3,
onSuccess: (MutableList<ArticleBean>?) -> Unit,
onFailure: (Throwable) -> Unit
) {
try {
val headers = mapOf("token" to token)
val params = mapOf(
"categoryId" to categoryId.toString(),
"pageNum" to pageNum.toString(),
"pageSize" to pageSize.toString(),
"showAppHomepage" to "Y"
)
api.getArticleList(headers, params)
.enqueue(object : Callback<ArticleListBean> {
override fun onResponse(
call: Call<ArticleListBean>,
response: Response<ArticleListBean>
) {
if (response.code() == 200) {
val body = response.body()
onSuccess(body?.rows)
} else {
onFailure(Exception("HTTP ${response.code()}: ${response.message()}"))
}
}
override fun onFailure(call: Call<ArticleListBean>, t: Throwable) {
onFailure(t)
}
})
} catch (e: Exception) {
onFailure(e)
}
}
/** 清除缓存的 Token(登录态变化时调用) */
fun clearToken() {
cachedToken = null
}
}
@@ -0,0 +1,15 @@
package com.xjjk.healthyclients.bean.emergency
data class GroupInfo(
val orderId: String,
val sessionId: String,
val salvageUserName: String,
val salvageUserSex: String,
val salvageUserAvatar: String,
val salvageUserAge: Int,
val salvageUserMobile: String,
val orgName: String,
val deptName: String,
val orderStatus: String,
val initTime: String
)
@@ -0,0 +1,53 @@
package com.xjjk.healthyclients.bean.home
import com.google.gson.annotations.SerializedName
/**
* 获取 token 响应
*/
data class TokenBean(
@SerializedName("token") val token: String? = null
)
/**
* 栏目分类
*/
data class CategoryBean(
@SerializedName("categoryId") val categoryId: Int = 0,
@SerializedName("categoryName") val categoryName: String? = null,
@SerializedName("categoryStatus") val categoryStatus: String? = null,
@SerializedName("children") val children: MutableList<CategoryBean>? = null,
@SerializedName("parentId") val parentId: Int = 0,
@SerializedName("sortOrder") val sortOrder: Int = 0
)
/**
* 文章
*/
data class ArticleBean(
@SerializedName("articleId") val articleId: Int = 0,
@SerializedName("articleTitle") val articleTitle: String? = null,
@SerializedName("articleSubtitle") val articleSubtitle: String? = null,
@SerializedName("articleTag") val articleTag: String? = null,
@SerializedName("articleHeaderimage") val articleHeaderimage: String? = null,
@SerializedName("articleType") val articleType: String? = null,
@SerializedName("articleExcerpt") val articleExcerpt: String? = null,
@SerializedName("articleContent") val articleContent: String? = null,
@SerializedName("articleVideo") val articleVideo: String? = null,
@SerializedName("favoriteCount") val favoriteCount: Int = 0,
@SerializedName("likeCount") val likeCount: Int = 0,
@SerializedName("viewCount") val viewCount: Int = 0,
@SerializedName("createTime") val createTime: String? = null,
@SerializedName("categoryId") val categoryId: Int = 0,
@SerializedName("articleScore") val articleScore: Double = 0.0,
@SerializedName("articleStatus") val articleStatus: String? = null,
@SerializedName("showAppHomepage") val showAppHomepage: String? = null
)
/**
* 文章列表分页响应
*/
data class ArticleListBean(
@SerializedName("rows") val rows: MutableList<ArticleBean>? = null,
@SerializedName("total") val total: Int = 0
)
@@ -52,3 +52,79 @@ data class HomeBannerBean(
data class EncryptInfoBean(
val encryptData: String?
)
data class HomeBankBean(
val todayPoints: String?,
val totalPoints: String?
)
data class ProposalBean(
val id: String,
val empId: String,
val name: String,
val age: Int,
val sexCode: String?,
val workUnit: String?,
val careLiaisonName: String?,
val idNo: String?,
val phone: String?,
val careStatus: String,
val dataLevel: Int,
val negativeList: String,
val caseInfo: String,
val riskLevel: String,
val year: String,
val flagFat: Int,
val bmi: String,
val height: String,
val weight: String,
val targetWeight: String,
val targetBmi: String,
val bmiCurrent: String,
val waistNew: String,
val bodyFatPercentNew: String,
val weightRiskType: String,
val hypertension: Int,
val systolicPressureAvg: String?,
val systolicPressureTarget: String?,
val systolicPressureCurrent: String?,
val diastolicPressureAvg: String?,
val diastolicPressureTarget: String?,
val diastolicPressureCurrent: String?,
val diabetes: Int,
val glu: String,
val bloodSugarTarget: String?,
val bloodSugarCurrent: String?,
val ldl: String,
val ldlTarget: String,
val tc: String,
val tcTarget: String,
val tg: String,
val tgTarget: String,
val uricAcid: String,
val uricAcidTarget: String,
val homocysteine: String,
val homocysteineTarget: String,
val bloodK: String,
val chd: String,
val cvd: String,
val af: String,
val ca: String,
val indicatorListStr: String,
val indicatorListStrOrigin: String,
val screeningProjects: String,
val treatPlan: String,
val treatPlanResponse: String,
val dietSuggestion: String,
val dietSuggestionResponse: String,
val sportGuide: String,
val sportGuideResponse: String,
val lifeManage: String,
val lifeManageResponse: String,
val cabinOpinion: String?,
val cabinOpinionTime: String,
val cabinOpinionUserName: String,
val expertOpinion: String?,
val expertOpinionTime: String,
val expertOpinionUserName: String
)
@@ -1,14 +1,20 @@
package com.xjjk.healthyclients.data.api
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.xjjk.healthyclients.bean.emergency.BigDiseaseBean
import com.xjjk.healthyclients.bean.emergency.BigDiseaseDetailsBean
import com.xjjk.healthyclients.bean.emergency.EmergencyGroupInfo
import com.xjjk.healthyclients.bean.emergency.GetOrderBySessionIdBean
import com.xjjk.healthyclients.bean.emergency.GroupInfo
import com.xjjk.healthyclients.bean.emergency.HospitalBean
import com.xjjk.healthyclients.bean.emergency.LocationResourceBean
import com.xjjk.healthyclients.bean.emergency.OrderThroughBean
import com.xjjk.healthyclients.bean.emergency.initUserOrderPageBean
import com.xjjk.healthyclients.bean.home.ProposalBean
import com.xjjk.healthyclients.data.bean.ApiResponse
import com.xjjk.healthyclients.data.bean.PageBean
import okhttp3.RequestBody
import retrofit2.http.Body
import retrofit2.http.GET
@@ -82,5 +88,51 @@ interface EmergencyApi {
*/
@GET("/health-emergency/api/emergency/createGroupMag")
suspend fun getIMGroupInfo(@QueryMap params: MutableMap<String, Any?>): ApiResponse<EmergencyGroupInfo>
/**
* 获取急救员群组信息
*/
@GET("/health-emergency/api/emergency/createGroupMagFirstAider")
suspend fun getParamedicGroupInfo(@QueryMap params: MutableMap<String, Any?>): ApiResponse<EmergencyGroupInfo>
/**
* 搜索专家(专业人员)
*/
@GET("/health-emergency/api/emergency/order/searchExpert")
suspend fun searchExpert(@QueryMap params: MutableMap<String, Any?>): ApiResponse<PageBean<ExpertBean>>
/**
* 搜索员工
*/
@GET("/health-emergency/api/emergency/order/searchEmployee")
suspend fun searchEmployee(@QueryMap params: MutableMap<String, Any?>): ApiResponse<PageBean<EmployeeBean>>
/**
* 实际群成员列表
*/
@GET("/health-emergency/api/emergency/order/getActualGroupMemberList")
suspend fun getActualGroupMemberList(@QueryMap params: MutableMap<String, Any?>): ApiResponse<List<GroupMemberBean>>
/**
* 邀请进群
*/
@POST("/health-emergency/api/emergency/addGroupUser")
suspend fun addGroupUser(@Body requestBody: RequestBody): ApiResponse<Boolean>
/**
* 移除群成员
*/
@POST("/health-emergency/api/emergency/removeGroupUser")
suspend fun removeGroupUser(@Body requestBody: RequestBody): ApiResponse<Boolean>
/**
* 获取一人一案
*/
@GET("/health-emergency/emergency/LargeScreenNew/onePersonCase")
suspend fun getProposalInfo(): ApiResponse<ProposalBean>
/**
* 关爱联络员身份判断
*/
@GET("/health-emergency/emergency/LargeScreenNew/isLiaisonUser")
suspend fun isLiaisonUser(): ApiResponse<Boolean>
/**
* 当前登录人参与的应急工单列表
*/
@GET("/health-emergency/api/emergency/order/myParticipatedOrders")
suspend fun getParticipatedOrders (): ApiResponse<PageBean<GroupInfo>>
}
@@ -0,0 +1,63 @@
package com.xjjk.healthyclients.data.api
import com.xjjk.healthyclients.bean.home.ArticleListBean
import com.xjjk.healthyclients.bean.home.CategoryBean
import com.xjjk.healthyclients.bean.home.TokenBean
import com.xjjk.healthyclients.retrofit.UrlConfig
import okhttp3.RequestBody
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.HeaderMap
import retrofit2.http.POST
import retrofit2.http.QueryMap
/**
* 健康资讯 API 接口(使用银行 Retrofit 实例,baseUrl 为 getDefaultBankBaseUrl()
*/
interface HealthInfoApi {
/**
* 获取 Token(带签名认证)
*/
@POST("${UrlConfig.middleCheck}pe/frontend/init")
fun getToken(
@HeaderMap headers: Map<String, String>,
@Body requestBody: RequestBody
): Call<TokenResponse>
/**
* 获取栏目列表
*/
@GET("${UrlConfig.middleCheck}cms/frontend/category/list")
fun getCategoryList(
@HeaderMap headers: Map<String, String>
): Call<CategoryListResponse>
/**
* 分页查询文章列表
*/
@GET("${UrlConfig.middleCheck}cms/frontend/articleList")
fun getArticleList(
@HeaderMap headers: Map<String, String>,
@QueryMap params: Map<String, String>
): Call<ArticleListBean>
}
/**
* Token 接口响应外层
*/
data class TokenResponse(
val msg: String? = null,
val code: Int = 0,
val data: TokenBean? = null
)
/**
* 栏目列表接口响应外层
*/
data class CategoryListResponse(
val msg: String? = null,
val code: Int = 0,
val data: MutableList<CategoryBean>? = null
)
@@ -1,10 +1,11 @@
package com.xjjk.healthyclients.data.api
import com.xjjk.healthyclients.bean.home.EncryptInfoBean
import com.xjjk.healthyclients.bean.home.HomeBankBean
import com.xjjk.healthyclients.bean.home.HomeBannerBean
import com.xjjk.healthyclients.bean.home.HomeNoticeBean
import com.xjjk.healthyclients.bean.home.HomeWatchAllDataBean
import com.xjjk.healthyclients.bean.home.WatchInfoBean
import com.xjjk.healthyclients.bean.home.EncryptInfoBean
import com.xjjk.healthyclients.bean.user.CheckInBean
import com.xjjk.healthyclients.bean.user.PointDetailBean
import com.xjjk.healthyclients.bean.user.PointRank
@@ -12,9 +13,12 @@ import com.xjjk.healthyclients.bean.user.PointRecord
import com.xjjk.healthyclients.bean.user.PointsTask
import com.xjjk.healthyclients.bean.user.UserPointBean
import com.xjjk.healthyclients.data.bean.ApiResponse
import com.xjjk.healthyclients.retrofit.UrlConfig
import okhttp3.RequestBody
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.HeaderMap
import retrofit2.http.POST
import retrofit2.http.QueryMap
@@ -92,5 +96,21 @@ interface HomeApi {
@GET("/health-system/api/sys/encryptInfo")
suspend fun getSysEnCryptInfo(@QueryMap params: MutableMap<String, Any?>): ApiResponse<EncryptInfoBean>
/**
* 绑定手表
*/
@GET("/health-watch/watch/watchDevice/app/bindDevice")
suspend fun getBindDevice(@QueryMap params: MutableMap<String, Any?>): ApiResponse<String>
/**
* 解绑手表
*/
@GET("/health-watch/watch/watchDevice/app/unbundleDevice")
suspend fun getUnbundleDevice(@QueryMap params: MutableMap<String, Any?>): ApiResponse<String>
/**
* 获取首页银行积分
*/
@POST("${UrlConfig.middle}api/v1/users/point-details")
fun postUserPointDetail(@HeaderMap headers :Map<String, String>, @Body requestBody: RequestBody): Call<ApiResponse<HomeBankBean>>
}
@@ -0,0 +1,21 @@
package com.xjjk.healthyclients.data.bean
/**
* 通用分页数据包装类
*
* 用于后端返回 MyBatis-Plus 风格的分页结构,实际列表数据位于 [records] 字段中。
*
* @param T 列表元素类型
*/
data class PageBean<T>(
/** 数据列表 */
val records: List<T>?,
/** 总记录数 */
val total: Int = 0,
/** 每页大小 */
val size: Int = 0,
/** 当前页码 */
val current: Int = 0,
/** 总页数 */
val pages: Int = 0
)
@@ -1,5 +1,8 @@
package com.xjjk.healthyclients.data.repository
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.xjjk.healthyclients.base.repository.BaseRepository
import com.xjjk.healthyclients.bean.CommonSettingMenuBean
import com.xjjk.healthyclients.bean.emergency.AddBigDiseaseSubmitBean
@@ -7,12 +10,15 @@ import com.xjjk.healthyclients.bean.emergency.BigDiseaseBean
import com.xjjk.healthyclients.bean.emergency.BigDiseaseDetailsBean
import com.xjjk.healthyclients.bean.emergency.EmergencyGroupInfo
import com.xjjk.healthyclients.bean.emergency.GetOrderBySessionIdBean
import com.xjjk.healthyclients.bean.emergency.GroupInfo
import com.xjjk.healthyclients.bean.emergency.HospitalBean
import com.xjjk.healthyclients.bean.emergency.LocationResourceBean
import com.xjjk.healthyclients.bean.emergency.OrderThroughBean
import com.xjjk.healthyclients.bean.emergency.initUserOrderPageBean
import com.xjjk.healthyclients.bean.home.ProposalBean
import com.xjjk.healthyclients.data.api.EmergencyApi
import com.xjjk.healthyclients.data.bean.ApiResponse
import com.xjjk.healthyclients.data.bean.PageBean
import com.xjjk.healthyclients.retrofit.RetrofitManager
import com.xjjk.healthyclients.retrofit.RetrofitManager.toRequestBody
import com.xjjk.healthyclients.superfuntion.toJson
@@ -107,8 +113,6 @@ object EmergencyRepository : BaseRepository() {
return CommonRepository.getCommonSettingMenuList("category")
}
suspend fun getIMGroupInfo(groupName: String, longitude: Double,
latitude: Double): ApiResponse<EmergencyGroupInfo> {
var map = mutableMapOf<String, Any?>()
@@ -118,4 +122,68 @@ object EmergencyRepository : BaseRepository() {
return apiCall { service.getIMGroupInfo(map) }
}
suspend fun getParamedicGroupInfo(groupName: String, longitude: Double,
latitude: Double): ApiResponse<EmergencyGroupInfo> {
var map = mutableMapOf<String, Any?>()
map["longitude"] = longitude
map["latitude"] = latitude
map["name"] = groupName //群聊名称改为后台生成
return apiCall { service.getParamedicGroupInfo(map) }
}
suspend fun searchExpert(realname: String, centerId: String, excludeSessionId: String, pageNo: Int, pageSize: Int): ApiResponse<PageBean<ExpertBean>> {
val map = mutableMapOf<String, Any?>()
map["realname"] = realname
map["centerId"] = centerId
map["excludeSessionId"] = excludeSessionId
map["pageNo"] = pageNo
map["pageSize"] = pageSize
return apiCall { service.searchExpert(map) }
}
suspend fun searchEmployee(realname: String, orgCode: String, workNo: String, phone: String, excludeSessionId: String, pageNo: Int, pageSize: Int): ApiResponse<PageBean<EmployeeBean>> {
val map = mutableMapOf<String, Any?>()
map["realname"] = realname
map["orgCode"] = orgCode
map["workNo"] = workNo
map["phone"] = phone
map["excludeSessionId"] = excludeSessionId
map["pageNo"] = pageNo
map["pageSize"] = pageSize
return apiCall { service.searchEmployee(map) }
}
suspend fun getActualGroupMemberList(sessionId: String): ApiResponse<List<GroupMemberBean>> {
val map = mutableMapOf<String, Any?>()
map["sessionId"] = sessionId
return apiCall { service.getActualGroupMemberList(map) }
}
suspend fun addGroupUser(groupId: String, memberList: List<String>,
memberType: Int): ApiResponse<Boolean> {
val map = mutableMapOf<String, Any?>()
map["groupId"] = groupId
map["memberList"] = memberList
map["memberType"] = memberType
return apiCall { service.addGroupUser(map.toJson().toRequestBody()) }
}
suspend fun removeGroupUser(groupId: String, memberList: List<String>): ApiResponse<Boolean> {
val map = mutableMapOf<String, Any?>()
map["groupId"] = groupId
map["memberList"] = memberList
return apiCall { service.removeGroupUser(map.toJson().toRequestBody()) }
}
suspend fun getProposalInfo(): ApiResponse<ProposalBean> {
return apiCall { service.getProposalInfo() }
}
suspend fun isLiaisonUser(): ApiResponse<Boolean> {
return apiCall { service.isLiaisonUser() }
}
suspend fun getParticipatedOrders(): ApiResponse<PageBean<GroupInfo>> {
return apiCall { service.getParticipatedOrders() }
}
}
@@ -109,4 +109,18 @@ object HomeRepository : BaseRepository() {
service.getSysEnCryptInfo(map)
}
}
suspend fun getBindDevice(watchNo: String?): ApiResponse<String> {
return apiCall {
val map = mutableMapOf<String, Any?>()
map["watchNo"] = watchNo
service.getBindDevice(map)
}
}
suspend fun getUnbundleDevice(watchNo: String?): ApiResponse<String> {
return apiCall {
val map = mutableMapOf<String, Any?>()
map["watchNo"] = watchNo
service.getUnbundleDevice(map)
}
}
}
@@ -1,19 +1,26 @@
package com.xjjk.healthyclients.fragment
import android.Manifest
import android.app.Dialog
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.view.WindowManager
import androidx.annotation.RequiresApi
import androidx.core.util.Consumer
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.amap.api.location.AMapLocation
import com.amap.api.location.AMapLocationClient
import com.amap.api.location.AMapLocationClientOption
@@ -30,12 +37,23 @@ import com.amap.api.maps.model.Marker
import com.amap.api.maps.model.MarkerOptions
import com.amap.api.maps.model.MyLocationStyle
import com.permissionx.guolindev.PermissionX
import com.tencent.qcloud.tuicore.util.SPUtils
import com.tencent.qcloud.tuikit.tuichat.TUIChatConstants.CHAT_IS_LIAISON_USER
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.tencent.qcloud.tuikit.tuichat.bean.WorkBean
import com.tencent.qcloud.tuikit.tuichat.classicui.interfaces.OnParamedicOperateListener
import com.tencent.qcloud.tuikit.tuichat.presenter.ParamedicOperateHelper
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.base.BaseVMBFragment
import com.xjjk.healthyclients.bean.emergency.EmergencyDictBean
import com.xjjk.healthyclients.bean.emergency.GroupInfo
import com.xjjk.healthyclients.bean.emergency.LocationResourceBean
import com.xjjk.healthyclients.data.repository.EmergencyRepository
import com.xjjk.healthyclients.databinding.DialogGroupInfoListBinding
import com.xjjk.healthyclients.databinding.FragmentEmergencyBinding
import com.xjjk.healthyclients.fragment.adapter.GroupInfoAdapter
import com.xjjk.healthyclients.superfuntion.getMarkerInfo
import com.xjjk.healthyclients.superfuntion.startGroupChat
import com.xjjk.healthyclients.ui.viewmodel.EmergencyViewModel
@@ -43,8 +61,12 @@ import com.xjjk.healthyclients.utils.IMInputActionSettingUtils
import com.xjjk.healthyclients.utils.MapUtils
import com.xjjk.healthyclients.utils.SystemFuntion.goNavigation
import com.xjjk.healthyclients.utils.TUIUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
/**
@@ -76,6 +98,100 @@ class EmergencyFragment :
)
var mListener: LocationSource.OnLocationChangedListener? = null
private val paramedicOperateListener = object : OnParamedicOperateListener {
override fun addGroupUser(
groupId: String,
memberList: List<String>,
memberType: Int,
callback: Consumer<Boolean>
) {
// 通过 successCall 把 ViewModel 的操作结果回传给 tuichat 模块
mViewModel.addGroupUser(groupId, memberList, memberType) { success ->
callback.accept(success)
}
}
override fun removeGroupUser(groupId: String, memberList: List<String>) {
mViewModel.removeGroupUser(groupId, memberList)
}
override fun searchEmployee(
realname: String?,
orgCode: String?,
workNo: String?,
phone: String?,
excludeSessionId: String?,
pageNo: Int,
pageSize: Int
): List<EmployeeBean?>? {
return runBlocking(Dispatchers.IO) {
try {
val deferred = async {
EmergencyRepository.searchEmployee(
realname ?: "",
orgCode ?: "",
workNo ?: "",
phone ?: "",
excludeSessionId ?: "",
pageNo,
pageSize
)
}
val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<EmployeeBean>()
response.takeIf { it.success }?.result?.records ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
}
override fun searchExpert(
realname: String?,
centerId: String?,
excludeSessionId: String?,
pageNo: Int,
pageSize: Int
): List<ExpertBean?>? {
return runBlocking(Dispatchers.IO) {
try {
val deferred = async {
EmergencyRepository.searchExpert(
realname ?: "",
centerId ?: "",
excludeSessionId ?: "",
pageNo,
pageSize
)
}
val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<ExpertBean>()
response.takeIf { it.success }?.result?.records ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
}
override fun getActualGroupMemberList(sessionId: String): List<GroupMemberBean?>? {
return runBlocking(Dispatchers.IO) {
try {
val deferred = async {
EmergencyRepository.getActualGroupMemberList(sessionId)
}
val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<GroupMemberBean>()
response.takeIf { it.success }?.result ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
}
}
override fun initView(root: View?, savedInstanceState: Bundle?) {
// fitTransparentStatusBar(mBinding.tvTitle)
MapsInitializer.updatePrivacyShow(context, true, true)
@@ -88,6 +204,15 @@ class EmergencyFragment :
PermissionX.init(this@EmergencyFragment)
.permissions(mPermissionList)
.request { allGranted, grantedList, deniedList -> setUpMap() }
ParamedicOperateHelper.getInstance().setListener(paramedicOperateListener)
mViewModel.isLiaisonUser { result ->
if (!result) {
mBinding.fragmentEmergencyParamedic.visibility = View.GONE
}
SPUtils.getInstance().put(CHAT_IS_LIAISON_USER, result)
}
}
override fun initData() {
@@ -97,13 +222,13 @@ class EmergencyFragment :
// 0.0,
// 0.0
// )
var mHeadType= mutableListOf<EmergencyDictBean>()
var mHeadType = mutableListOf<EmergencyDictBean>()
// mHeadType.add(EmergencyDictBean("全部","全部","全部","0",true))
mHeadType.add(EmergencyDictBean("油田医院","油田医院","油田医院","1",true))
mHeadType.add(EmergencyDictBean("油田医院", "油田医院", "油田医院", "1", true))
// mHeadType.add(EmergencyDictBean("合作医院","合作医院","合作医院","2",false))
mHeadType.add(EmergencyDictBean("健康小屋","健康小屋","健康小屋","3",false))
mHeadType.add(EmergencyDictBean("AED","AED","AED","4",false))
mHeadType.add(EmergencyDictBean("救护车","救护车","救护车","5",false))
mHeadType.add(EmergencyDictBean("健康小屋", "健康小屋", "健康小屋", "3", false))
mHeadType.add(EmergencyDictBean("AED", "AED", "AED", "4", false))
mHeadType.add(EmergencyDictBean("救护车", "救护车", "救护车", "5", false))
mBinding.fragmentEmergencyHeadRv.setHeadData(mHeadType) { titleBean ->
// if (titleBean.value.equals("3")) {
@@ -119,6 +244,8 @@ class EmergencyFragment :
// }
}
titleId = mHeadType[0].value
mViewModel.getEmergencyData(mHeadType[0].value, mCurrentLat, mCurrentLon)
}
override fun createObserve() {
@@ -168,6 +295,7 @@ class EmergencyFragment :
super.onResume()
mMapView?.onResume()
}
override fun onPause() {
super.onPause()
mMapView?.onPause()
@@ -227,6 +355,7 @@ class EmergencyFragment :
fragmentEmergencyPhone.setOnClickListener(this@EmergencyFragment)
fragmentEmergencyCardNavigation.setOnClickListener(this@EmergencyFragment)
fragmentEmergencyMarkerInfo.setOnClickListener(this@EmergencyFragment)
fragmentEmergencyParamedic.setOnClickListener(this@EmergencyFragment)
}
}
@@ -284,6 +413,25 @@ class EmergencyFragment :
)
})
}
R.id.fragment_emergency_paramedic -> {
mViewModel.getParticipatedOrders { result ->
when {
result.records.isNullOrEmpty() -> {
showToast("暂无参与的应急就医工单")
}
result.records.size == 1 -> {
startParamedicChat(result.records[0])
}
else -> {
showChatListDialog(result.records) { groupInfo ->
startParamedicChat(groupInfo)
}
}
}
}
}
R.id.fragment_emergency_marker_info -> {
}
@@ -317,6 +465,90 @@ class EmergencyFragment :
}
}
/**
* 启动 medic 应急就医群聊:拉取群成员 → 配置 IM 输入态 → 进入群聊界面
*/
private fun startParamedicChat(groupInfo: GroupInfo) {
mViewModel.getActualGroupMemberList(groupInfo.sessionId, successCall = { groupMemberBeans ->
IMInputActionSettingUtils.createEmergencySetting(true)
activity?.startGroupChat(
groupInfo.sessionId,
getString(R.string.title_paramedic_group),
false,
groupMemberBeans.map { it.userId }.toList() as ArrayList<String>?,
workBean = WorkBean(groupInfo.orderId, TUIUtils.WORK_TYPE_EMERGENCY)
)
})
}
/**
* 展示急救工单选择Dialog(Figma设计稿)
*/
private fun showChatListDialog(
records: List<GroupInfo>,
onChatSelected: (GroupInfo) -> Unit
) {
context?.let { ctx ->
val dialog = Dialog(ctx, com.xjjk.healthyclients.R.style.EmergencyDialogTheme)
val binding = DialogGroupInfoListBinding.inflate(layoutInflater)
val adapter = GroupInfoAdapter(records) { selectedPos ->
// 选中变化时更新确认按钮状态
val hasSelection = selectedPos >= 0
binding.tvConfirm.isEnabled = hasSelection
binding.tvConfirm.alpha = if (hasSelection) 1.0f else 0.4f
}
binding.rvGroupList.layoutManager = LinearLayoutManager(ctx)
binding.rvGroupList.adapter = adapter.apply {
// 初始禁用确认按钮
binding.tvConfirm.isEnabled = false
binding.tvConfirm.alpha = 0.4f
}
// 列表项间距
val spacing = ctx.resources.getDimensionPixelSize(com.xjjk.healthyclients.R.dimen.dp_12)
binding.rvGroupList.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
outRect.bottom = spacing
}
})
// 确认按钮点击
binding.tvConfirm.setOnClickListener {
adapter.selectedItem?.let { item ->
onChatSelected(item)
dialog.dismiss()
}
}
// 右上角X关闭
binding.ivClose.setOnClickListener { dialog.dismiss() }
// 点击卡片外部区域关闭
binding.flRoot.setOnClickListener { dialog.dismiss() }
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(true)
dialog.setCancelable(true)
dialog.window?.let { window ->
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
window.decorView.setBackgroundColor(Color.TRANSPARENT)
window.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT
)
}
dialog.show()
}
}
fun setMarker(markerList: MutableList<LocationResourceBean>) {
mAMap?.clear() //清除点位
if (markerList.isNullOrEmpty()) {
@@ -350,7 +582,11 @@ class EmergencyFragment :
}
if (markerList.size == 1) {
MapUtils.setMapZoonloAndZoom(mAMap, LatLng(markerList.get(0).latitude, markerList.get(0).longitude),11)
MapUtils.setMapZoonloAndZoom(
mAMap,
LatLng(markerList.get(0).latitude, markerList.get(0).longitude),
11
)
} else {
MapUtils.setMapZoonlo(mAMap, 200, ArrayList(newlist))
}
@@ -14,20 +14,29 @@ import com.sw.healthyclients.utils.DateUtil
import com.tencent.qcloud.tuikit.tuichat.bean.WorkBean
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.adapter.common.MultiItemTypeAdapter
import com.xjjk.healthyclients.bankRequest.BankPointRequest
import com.xjjk.healthyclients.bankRequest.HealthInfoRequest
import com.xjjk.healthyclients.base.BaseVMBFragment
import com.xjjk.healthyclients.bean.guidance.GuidanceListBean
import com.xjjk.healthyclients.bean.home.CategoryBean
import com.xjjk.healthyclients.bean.home.WatchInfoBean
import com.xjjk.healthyclients.data.api.HomeApi
import com.xjjk.healthyclients.databinding.FragmentHomeBinding
import com.xjjk.healthyclients.event.HomeTabChangeEvent
import com.xjjk.healthyclients.retrofit.UrlConfig
import com.xjjk.healthyclients.retrofit.getBankRetrofit
import com.xjjk.healthyclients.superfuntion.initColors
import com.xjjk.healthyclients.superfuntion.orEmptyDefault
import com.xjjk.healthyclients.superfuntion.showBanner
import com.xjjk.healthyclients.superfuntion.startAppointmentWaitAffirmActivity
import com.xjjk.healthyclients.superfuntion.startGroupChat
import com.xjjk.healthyclients.superfuntion.startLoginActivity
import com.xjjk.healthyclients.superfuntion.startMyGuidanceActivity
import com.xjjk.healthyclients.superfuntion.startWebActivity
import com.xjjk.healthyclients.superfuntion.toZeroDefault
import com.xjjk.healthyclients.ui.activity.EmptyActivity
import com.xjjk.healthyclients.ui.activity.LoginActivity
import com.xjjk.healthyclients.ui.activity.ProposalActivity
import com.xjjk.healthyclients.ui.activity.guidance.adapter.GuidanceFragmentDoctorAdapter
import com.xjjk.healthyclients.ui.activity.home.HomeBannerDetailActivity
import com.xjjk.healthyclients.ui.activity.home.HomeNoticeListActivity
@@ -35,6 +44,7 @@ import com.xjjk.healthyclients.ui.viewmodel.HomeViewModel
import com.xjjk.healthyclients.utils.ConstantUtils
import com.xjjk.healthyclients.utils.IMInputActionSettingUtils
import com.xjjk.healthyclients.utils.TUIUtils
import com.xjjk.healthyclients.view.WatchBindDialog
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
@@ -48,12 +58,26 @@ class HomeFragment :
BaseVMBFragment<HomeViewModel, FragmentHomeBinding>(R.layout.fragment_home),
SwipeRefreshLayout.OnRefreshListener {
private var mApi = getBankRetrofit().create(HomeApi::class.java)
var watchBindDialog: WatchBindDialog? = null
var stringList: MutableList<String> = mutableListOf()
var mDoctorList = arrayListOf<GuidanceListBean>()
var mGuidanceFragmentDoctorAdapter: GuidanceFragmentDoctorAdapter? = null
var REQUEST_DURATION = 1000 * 120L
var noticeTime = REQUEST_DURATION
var watchInfo: WatchInfoBean? = null
/** 健康资讯请求 */
private var mHealthInfoRequest: HealthInfoRequest? = null
/** 健康资讯 Token */
private var mHealthToken: String? = null
/** 健康知识子栏目列表 */
private var mKnowledgeCategories: MutableList<CategoryBean> = mutableListOf()
/** 通知轮播协程 Job,重新启动前先取消旧任务,防止多协程并发叠加 */
private var noticeJob: Job? = null
@@ -62,6 +86,14 @@ class HomeFragment :
override fun initView(root: View?, savedInstanceState: Bundle?) {
mBinding.swipeRefresh.initColors()
mBinding.swipeRefresh.setOnRefreshListener(this@HomeFragment)
watchBindDialog = WatchBindDialog(requireContext()) { watchName, type ->
// 0 绑定 1 解绑
if (type == 0) {
mViewModel.getBindDevice(watchName)
// } else {
// mViewModel.getUnbundleDevice(watchName)
}
}
var manager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
mBinding.layoutItem3.fragmentGuidanceRvDoctor.layoutManager = manager
@@ -120,6 +152,7 @@ class HomeFragment :
})
mBinding.layoutItem3.fragmentGuidanceRvDoctor.adapter = mGuidanceFragmentDoctorAdapter
mBinding.watchView.init(this@HomeFragment)
setupHealthViews()
}
override fun bindEvent() {
@@ -136,43 +169,40 @@ class HomeFragment :
layoutItem1.homeHeadTab4, layoutItem3.tvDoctorMore
)
}
mBinding.watchView.mBinding.layoutWatchMore.setOnClickListener{
mBinding.watchView.mBinding.imgWatchMore.setOnClickListener {
if (DataStoreManager.isLogin()) {
if (watchInfo?.hasWatch == true) {
EventBus.getDefault().post(HomeTabChangeEvent().apply { index = 3 })
} else {
showWatchBindDialog()
}
} else {
toActivity(LoginActivity::class.java)
}
}
mBinding.watchView.mBinding.watchName.setOnClickListener {
if (DataStoreManager.isLogin()) {
// if (watchInfo?.hasWatch==true){
// showWatchBindDialog(watchInfo?.watchNo)
// }
} else {
toActivity(LoginActivity::class.java)
}
}
mBinding.watchView.mBinding.btnBindWatch.setOnClickListener {
showWatchBindDialog()
}
}
private fun showWatchBindDialog(watchId: String? = null) {
watchBindDialog?.showWatchInfo(watchId)
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.img_left -> {
val bundle = Bundle()
bundle.putString("title", "智慧体重")
toActivity(EmptyActivity::class.java, bundle)
}
R.id.img_right -> {
val bundle = Bundle()
bundle.putString("title", "一人一案")
toActivity(EmptyActivity::class.java, bundle)
}
R.id.home_head_tab1 -> {
val bundle = Bundle()
bundle.putString("title", "健康档案")
toActivity(EmptyActivity::class.java, bundle)
}
R.id.home_head_tab2 -> {
val bundle = Bundle()
bundle.putString("title", "健康体检")
toActivity(EmptyActivity::class.java, bundle)
}
R.id.home_head_tab4 -> {
if (UrlConfig.baseUrlType == UrlConfig.BaseUrlType.TEST) {
if (DataStoreManager.isLogin()) {
val value = mViewModel.enCryptInfoUrl.value
if (TextUtils.isEmpty(value)) {
@@ -180,16 +210,82 @@ class HomeFragment :
showToast("网络错误")
return
}
requireContext().startWebActivity(UrlConfig.HOME_BANK_H5+"?param="+ value, myTitle = "健康银行")
requireContext().startWebActivity(
UrlConfig.getWeightManagementH5Url() + "?param=" + value,
isFull = true,
myTitle = "",
hideStatus = true
)
} else {
startLoginActivity(requireContext())
}
return
}
R.id.img_right -> {
if (DataStoreManager.isLogin()) {
val bundle = Bundle()
bundle.putString("title", "健康银行")
toActivity(EmptyActivity::class.java, bundle)
bundle.putString("title", "一人一案")
toActivity(ProposalActivity::class.java, bundle)
} else {
startLoginActivity(requireContext())
}
}
R.id.home_head_tab1 -> {
if (DataStoreManager.isLogin()) {
val value = mViewModel.enCryptInfoUrl.value
if (TextUtils.isEmpty(value)) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
return
}
requireContext().startWebActivity(
UrlConfig.getHealthFileH5Url() + "?param=" + value,
isFull = true,
myTitle = "",
hideStatus = true
)
} else {
startLoginActivity(requireContext())
}
}
R.id.home_head_tab2 -> {
if (DataStoreManager.isLogin()) {
val value = mViewModel.enCryptInfoUrl.value
if (TextUtils.isEmpty(value)) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
return
}
requireContext().startWebActivity(
UrlConfig.getHealthCheckH5Url() + "?param=" + value,
isFull = true,
myTitle = "健康体检",
hideStatus = true
)
} else {
startLoginActivity(requireContext())
}
}
R.id.home_head_tab4 -> {
if (DataStoreManager.isLogin()) {
val value = mViewModel.enCryptInfoUrl.value
if (TextUtils.isEmpty(value)) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
return
}
requireContext().startWebActivity(
UrlConfig.getBankH5Url() + "?param=" + value,
isFull = true,
myTitle = "健康银行",
hideStatus = true
)
} else {
startLoginActivity(requireContext())
}
}
@@ -221,6 +317,7 @@ class HomeFragment :
override fun onResume() {
super.onResume()
mBinding.healthKnowledgeView.clearSearchFocus()
if (DataStoreManager.isLogin()) {
mBinding.tvHi.visibility = View.VISIBLE
// mBinding.layoutNotice.visibility = View.VISIBLE
@@ -256,10 +353,22 @@ class HomeFragment :
}.sortedBy { homeBannerBean -> homeBannerBean.sort }
showBanner(mBinding.banner1, bannerList1) {
val bundle = Bundle()
bundle.putString("title", "体检套餐")
toActivity(EmptyActivity::class.java, bundle)
if (DataStoreManager.isLogin()) {
val value = mViewModel.enCryptInfoUrl.value
if (TextUtils.isEmpty(value)) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
} else {
requireContext().startWebActivity(
UrlConfig.getHealthCheckH5Url() + "?param=" + value,
isFull = true,
myTitle = "健康体检",
hideStatus = true
)
}
} else {
startLoginActivity(requireContext())
}
}
showBanner(mBinding.banner2, bannerList2) {
@@ -273,10 +382,27 @@ class HomeFragment :
startLoginActivity(requireContext())
}
}
showBanner(mBinding.banner4, bannerList4) {
val bundle = Bundle()
bundle.putString("title", "AI随访")
toActivity(EmptyActivity::class.java, bundle)
showBanner(mBinding.banner4, bannerList4) {banner->
if (DataStoreManager.isLogin()) {
if (banner.jumpUrl.isEmpty()) {
toActivity(EmptyActivity::class.java, Bundle())
} else {
val value = mViewModel.enCryptInfoUrl.value
if (TextUtils.isEmpty(value)) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
} else {
requireContext().startWebActivity(
banner.jumpUrl + "?param=" + value,
isFull = true,
myTitle = "健康体检",
hideStatus = true
)
}
}
} else {
startLoginActivity(requireContext())
}
}
@@ -322,24 +448,34 @@ class HomeFragment :
}
}
}
lifecycleScope.launch{
repeatOnLifecycle(Lifecycle.State.CREATED){
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
mViewModel.watchInfo.collectLatest { bean ->
watchInfo = bean
mBinding.watchView.setWatchInfo(bean)
if (bean?.hasWatch==true) {//有手表的情况下去获取手表数据
if (bean?.hasWatch == true) {//有手表的情况下去获取手表数据
mViewModel.getHomeWatchData(DateUtil.nowStringDateShort)
}
}
}
}
lifecycleScope.launch{
repeatOnLifecycle(Lifecycle.State.CREATED){
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
mViewModel.watchAllData.collectLatest { bean ->
mBinding.watchView.setWatchAllData(bean)
}
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
mViewModel.watchBindStatus.collectLatest {
showToast(it)
watchBindDialog?.cancel()
mViewModel.getHomeWatchInfo(DataStoreManager.getUserId())
}
}
}
// lifecycleScope.launch{
// repeatOnLifecycle(Lifecycle.State.CREATED){
// mViewModel.enCryptInfoUrl.collectLatest { url ->
@@ -353,15 +489,41 @@ class HomeFragment :
}
/**
* 获取银行积分详情
*/
private fun getBankPointDetail() {
val bankPointRequest = BankPointRequest(mApi)
bankPointRequest.getBankPointDetail(
DataStoreManager.getUserId().orEmptyDefault(""),
onSuccess = { bean ->
// 处理成功响应
mBinding.layoutItem1.tvTodayPoints.text = "${bean?.todayPoints.toZeroDefault()}"
mBinding.layoutItem1.tvTotalPoints.text = "${bean?.totalPoints.toZeroDefault()}"
},
onFailure = { error ->
showToast("网络错误")
}
)
}
override fun initData() {
mViewModel.getHomeBannerList()
mViewModel.selectDoctorRecommendHome()
if (DataStoreManager.isLogin()) {
homeNoticeStartRepeat()
mViewModel.selectDoctorRecommendHome()
mViewModel.getHomeWatchInfo(DataStoreManager.getUserId())
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
getBankPointDetail()
loadHealthInfoData()
} else {
mBinding.healthKnowledgeView.mBinding.tabLayout.visibility = View.GONE
mBinding.healthKnowledgeView.mBinding.tvEmpty.visibility = View.VISIBLE
mBinding.healthKnowledgeView.mBinding.tvEmpty.text = "暂未登录"
}
}
@@ -385,7 +547,206 @@ class HomeFragment :
}
if (DataStoreManager.isLogin()) {
mViewModel.getHomeNoticeListRepeat()
mViewModel.selectDoctorRecommendHome()
mViewModel.getHomeWatchInfo(DataStoreManager.getUserId())
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
getBankPointDetail()
loadHealthInfoData()
}
}
// ==================== 健康资讯相关 ====================
/** 初始化健康资讯两个 View 的事件回调 */
private fun setupHealthViews() {
mHealthInfoRequest = HealthInfoRequest()
// 健康知识 - Tab 切换加载对应栏目文章
mBinding.healthKnowledgeView.setOnTabSelectedListener { category ->
loadKnowledgeArticles(category.categoryId)
}
// 健康知识 - 列表项点击 → 跳转文章详情 H5
mBinding.healthKnowledgeView.setOnItemClickListener { article ->
navigateToArticleDetail(article.articleId)
}
// 健康知识 - 更多按钮 → 跳转健康知识 H5 页面
mBinding.healthKnowledgeView.setOnMoreClickListener {
navigateToHealthKnowledgeMore()
}
// 健康知识 - 搜索
mBinding.healthKnowledgeView.setOnSearchClickListener {
navigateToHealthKnowledgeSearch(it)
}
// 健康咨询 - 列表项点击 → 跳转文章详情 H5
mBinding.healthConsultationView.setOnItemClickListener { article ->
navigateToArticleDetail(article.articleId)
}
// 健康咨询 - 更多按钮 → 跳转健康咨询 H5 页面
mBinding.healthConsultationView.setOnMoreClickListener {
navigateToHealthConsultationMore()
}
}
/** 加载健康资讯数据(Token → 栏目 → 文章) */
private fun loadHealthInfoData() {
mBinding.healthKnowledgeView.mBinding.tabLayout.visibility = View.VISIBLE
mBinding.healthKnowledgeView.mBinding.tvEmpty.visibility = View.GONE
mBinding.healthKnowledgeView.mBinding.tvEmpty.text = "暂无健康知识"
mHealthInfoRequest?.fetchToken(
onSuccess = { token ->
mHealthToken = token
loadCategories(token)
},
onFailure = { /* Token 获取失败,静默处理 */ }
)
}
/** 加载栏目列表并设置健康知识 Tab */
private fun loadCategories(token: String) {
mHealthInfoRequest?.fetchCategoryList(
token,
onSuccess = { categories ->
// 查找健康知识栏目(categoryId=1)的子栏目
val knowledgeCategory = categories.find { it.categoryId == 1 }
mKnowledgeCategories.clear()
knowledgeCategory?.children?.let { mKnowledgeCategories.addAll(it) }
mBinding.healthKnowledgeView.setupTabs(mKnowledgeCategories)
// 默认加载第一个子栏目文章
if (mKnowledgeCategories.isNotEmpty()) {
loadKnowledgeArticles(mKnowledgeCategories[0].categoryId)
}
if (categories.isNotEmpty() && categories.size >= 2) {
loadConsultationArticles(categories[1].categoryId)
}
},
onFailure = { /* 栏目加载失败,静默处理 */ }
)
}
/** 加载健康知识指定栏目下的文章列表 */
private fun loadKnowledgeArticles(categoryId: Int) {
mHealthToken?.let { token ->
mHealthInfoRequest?.fetchArticleList(
token, categoryId,
onSuccess = { articles ->
mBinding.healthKnowledgeView.setArticles(articles)
},
onFailure = { /* 文章加载失败,静默处理 */ }
)
}
}
/** 加载健康咨询文章列表(categoryId */
private fun loadConsultationArticles(categoryId: Int) {
mHealthToken?.let { token ->
mHealthInfoRequest?.fetchArticleList(
token, categoryId,
onSuccess = { articles ->
mBinding.healthConsultationView.setArticles(articles)
},
onFailure = { /* 文章加载失败,静默处理 */ }
)
}
}
/** 跳转文章详情 H5 */
private fun navigateToArticleDetail(articleId: Int) {
if (DataStoreManager.isLogin()) {
val param = mViewModel.enCryptInfoUrl.value
if (param.isNullOrEmpty()) {
showToast("网络错误")
return
}
val url = "${UrlConfig.getHealthNewsDetailH5Url()}?param=$param&articleId=$articleId"
requireContext().startWebActivity(
url,
isFull = true,
myTitle = "文章详情",
hideStatus = true
)
} else {
showToast("请先登录")
toActivity(LoginActivity::class.java)
}
}
/** 跳转健康知识更多 H5 */
private fun navigateToHealthKnowledgeMore() {
if (DataStoreManager.isLogin()) {
val param = mViewModel.enCryptInfoUrl.value
if (param.isNullOrEmpty()) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
return
}
val url = "${UrlConfig.getHealthKnowledgeH5Url()}?param=$param"
requireContext().startWebActivity(
url,
isFull = true,
myTitle = "健康知识",
hideStatus = true
)
} else {
showToast("请先登录")
toActivity(LoginActivity::class.java)
}
}
/** 跳转健康知识 搜索 */
private fun navigateToHealthKnowledgeSearch(key: String) {
if (DataStoreManager.isLogin()) {
val param = mViewModel.enCryptInfoUrl.value
if (param.isNullOrEmpty()) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
return
}
val url = "${UrlConfig.getHealthNewsSearchH5Url()}?param=$param&secrchVal=$key"
requireContext().startWebActivity(
url,
isFull = true,
myTitle = "搜索",
hideStatus = true
)
} else {
showToast("请先登录")
toActivity(LoginActivity::class.java)
}
}
/** 跳转健康咨询更多 H5 */
private fun navigateToHealthConsultationMore() {
if (DataStoreManager.isLogin()) {
val param = mViewModel.enCryptInfoUrl.value
if (param.isNullOrEmpty()) {
mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url())
showToast("网络错误")
return
}
val url = "${UrlConfig.getHealthConsultationH5Url()}?param=$param"
requireContext().startWebActivity(
url,
isFull = true,
myTitle = "健康咨询",
hideStatus = true
)
} else {
showToast("请先登录")
toActivity(LoginActivity::class.java)
}
}
@@ -0,0 +1,118 @@
package com.xjjk.healthyclients.fragment.adapter
import android.content.res.ColorStateList
import android.graphics.Color
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.xjjk.healthyclients.bean.emergency.GroupInfo
import com.xjjk.healthyclients.databinding.ItemGroupInfoBinding
/**
* 急救工单选择适配器,支持单选
*/
class GroupInfoAdapter(
private val items: List<GroupInfo>,
private val onSelectionChanged: (Int) -> Unit
) : RecyclerView.Adapter<GroupInfoAdapter.ViewHolder>() {
/** 当前选中位置,-1 表示无选中 */
var selectedPosition = -1
private set
/** 获取当前选中的工单 */
val selectedItem: GroupInfo?
get() = if (selectedPosition in items.indices) items[selectedPosition] else null
/** 选中主色 */
private var colorPrimary = 0
/** 未选中灰色 */
private var colorGray = 0
private var colorDark = 0
private var colorBody = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemGroupInfoBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
// 初始化颜色值
if (colorPrimary == 0) {
colorPrimary = ContextCompat.getColor(parent.context, com.xjjk.healthyclients.R.color.emergency_primary)
colorGray = ContextCompat.getColor(parent.context, com.xjjk.healthyclients.R.color.emergency_text_gray)
colorDark = ContextCompat.getColor(parent.context, com.xjjk.healthyclients.R.color.emergency_text_dark)
colorBody = ContextCompat.getColor(parent.context, com.xjjk.healthyclients.R.color.emergency_text_body)
}
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position], position == selectedPosition)
}
override fun getItemCount(): Int = items.size
inner class ViewHolder(private val binding: ItemGroupInfoBinding) :
RecyclerView.ViewHolder(binding.root) {
private var currentPosition = -1
init {
binding.rootItem.setOnClickListener {
val oldPos = selectedPosition
if (oldPos == currentPosition) {
// 取消选中
selectedPosition = -1
notifyItemChanged(currentPosition)
onSelectionChanged(-1)
} else {
selectedPosition = currentPosition
notifyItemChanged(oldPos)
notifyItemChanged(currentPosition)
onSelectionChanged(currentPosition)
}
}
}
fun bind(groupInfo: GroupInfo, isSelected: Boolean) {
currentPosition = adapterPosition
val ctx = binding.root.context
// 姓名
binding.tvUserName.text = groupInfo.salvageUserName
// 部门标签
binding.tvDeptName.text = groupInfo.deptName
// 地址
binding.tvOrgName.text = groupInfo.orgName
// 时间
binding.tvInitTime.text = "求助时间:${groupInfo.initTime}"
if (isSelected) {
// 选中态
binding.rootItem.setBackgroundResource(com.xjjk.healthyclients.R.drawable.bg_emergency_item_selected)
binding.ivRadio.setImageResource(com.xjjk.healthyclients.R.drawable.ic_emergency_radio_selected)
binding.tvDeptName.setTextColor(colorPrimary)
binding.tvDeptName.setBackgroundResource(com.xjjk.healthyclients.R.drawable.bg_dept_badge_selected)
binding.tvInitTime.setTextColor(colorPrimary)
binding.ivLocation.imageTintList = ColorStateList.valueOf(colorPrimary)
binding.ivClock.imageTintList = ColorStateList.valueOf(colorPrimary)
} else {
// 未选中态
binding.rootItem.setBackgroundResource(com.xjjk.healthyclients.R.drawable.bg_emergency_item_unselected)
binding.ivRadio.setImageResource(com.xjjk.healthyclients.R.drawable.ic_emergency_radio_unselected)
binding.tvDeptName.setTextColor(colorGray)
binding.tvDeptName.setBackgroundResource(com.xjjk.healthyclients.R.drawable.bg_dept_badge_unselected)
binding.tvInitTime.setTextColor(colorGray)
binding.ivLocation.imageTintList = ColorStateList.valueOf(colorGray)
binding.ivClock.imageTintList = ColorStateList.valueOf(colorGray)
}
}
}
}
@@ -0,0 +1,23 @@
package com.xjjk.healthyclients.retrofit
import com.xjjk.healthyclients.retrofit.UrlConfig.getDefaultBankBaseUrl
import com.xjjk.healthyclients.utils.ApiDns
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
fun getBankRetrofit(): Retrofit {
val builder = OkHttpClient.Builder()
builder.connectTimeout(30, TimeUnit.SECONDS)
builder.readTimeout(30, TimeUnit.SECONDS)
// builder.addInterceptor(LoggingInterceptor2())
builder.dns(ApiDns())
var baseUrl = getDefaultBankBaseUrl()
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(builder.build())
.build()
}
@@ -1,7 +1,6 @@
package com.xjjk.healthyclients.retrofit
import com.sw.healthyclients.data.local.DataStoreManager
import com.xjjk.healthyclients.utils.UrlH5RouteUtils
import okhttp3.HttpUrl.Companion.toHttpUrl
object UrlConfig {
@@ -15,12 +14,13 @@ object UrlConfig {
private const val DEBUG_DEFAULT_IP_ADDRESS_REMOTE = "http://192.168.1.47" // 开发环境
// private const val DEBUG_DEFAULT_IP_ADDRESS_REMOTE = "http://api-xj.tri.dt.io/" // 开发环境
private const val TEST_DEFAULT_IP_ADDRESS_REMOTE = "https://xj-api.mcrm.vip:8888" // 测试环境
// private const val TEST_DEFAULT_IP_ADDRESS_REMOTE = "https://xj-api.mcrm.vip:8888" // 测试环境
private const val TEST_DEFAULT_IP_ADDRESS_REMOTE = "https://xj-api.yixiong-tech.com:8081" // 测试环境
// private const val PRODUCT_DEFAULT_IP_ADDRESS_REMOTE = "https://api.xjygjk.com" // 正式环境
private const val PRODUCT_DEFAULT_IP_ADDRESS_REMOTE = "https://api-jkglpt.iosp.ydpt.tech" // 正式环境
private const val DEBUG_H5_ADDRESS_REMOTE = "http://console-xj.tri.dt.io" // 开发环境
private const val TEST_H5_ADDRESS_REMOTE = "https://xj-admin.mcrm.vip:8888" // 测试环境
private const val TEST_H5_ADDRESS_REMOTE = "http://10.10.10.228:23006" // 测试环境
// private const val PRODUCT_H5_ADDRESS_REMOTE = "https://console.xjygjk.com" // 正式环境
private const val PRODUCT_H5_ADDRESS_REMOTE = "https://console-jkglpt.iosp.ydpt.tech" // 正式环境
//2025年10月23日 14:37 冯普说改为阿里云的地址,给油田部署的正式地址还没有启用
@@ -28,11 +28,33 @@ object UrlConfig {
// 安眼修改密码
const val ANYAN_CHANGE_PASSWORD_H5 = "https://sso.iosp.ydpt.tech/SSOManageClient/userController/changePassword"
const val HOME_BANK_H5 = "https://gstest.superwx.cn/healthbank/index.html"
//健康银行
private const val TEST_HOME_BANK_H5 = "https://gstest.superwx.cn/healthbank/index.html"
private const val PRODUCT_HOME_BANK_H5 = "https://console-jkglpt.iosp.ydpt.tech/hb/healthweb/index.html"
//健康体检
private const val PRODUCT_HOME_HEALTH_CHECK_H5 = "https://console-jkglpt.iosp.ydpt.tech/hb/healthcheckweb/index.html"
private const val TEST_HOME_HEALTH_CHECK_H5 = "https://gstest.superwx.cn/healthcheckweb/index.html"
//体重智能管理
private const val PRODUCT_HOME_WEIGHT_MANAGEMENT_H5 = "https://console-jkglpt.iosp.ydpt.tech/hb/healthweightweb/index.html"
//健康档案
private const val PRODUCT_HOME_HEALTH_FILE_H5 = "https://console-jkglpt.iosp.ydpt.tech/hb/healthfileweb/index.html"
//手机银行
const val SECRET_KEY = "XJp3EHpcnwReXc1A"
private const val TEST_HOME_BANK_IP = "https://gstest.superwx.cn/"
val baseUrlType: BaseUrlType = BaseUrlType.PRODUCT
// const val middle = "xj_health/" //test
// const val middleCheck = "healthcheck/" //test
const val middle = "hb/" //product
const val middleCheck = "hb/" //product
var isOpenIm: Boolean = true
enum class BaseUrlType(val type: Int) {
@@ -56,6 +78,19 @@ object UrlConfig {
}
}
}
private fun getBankBaseUrl(baseUrlType: BaseUrlType = BaseUrlType.PRODUCT): String {
return when (baseUrlType) {
BaseUrlType.DEBUG, BaseUrlType.TEST -> {
TEST_HOME_BANK_IP
}
BaseUrlType.PRODUCT -> {
PRODUCT_DEFAULT_IP_ADDRESS_REMOTE
}
}
}
fun getH5BaseUrl(baseUrlType: BaseUrlType = BaseUrlType.PRODUCT): String {
return when (baseUrlType) {
BaseUrlType.DEBUG -> {
@@ -71,6 +106,97 @@ object UrlConfig {
}
}
}
fun getH5BaseXjUrl(baseUrlType: BaseUrlType = BaseUrlType.PRODUCT): String {
return when (baseUrlType) {
BaseUrlType.DEBUG, BaseUrlType.TEST -> {
TEST_HOME_BANK_IP
}
BaseUrlType.PRODUCT -> {
"$PRODUCT_H5_ADDRESS_REMOTE/hb/"
}
}
}
fun getBankH5Url(baseUrlType: BaseUrlType = BaseUrlType.PRODUCT): String {
return when (baseUrlType) {
BaseUrlType.DEBUG, BaseUrlType.TEST-> {
TEST_HOME_BANK_H5
}
BaseUrlType.PRODUCT -> {
PRODUCT_HOME_BANK_H5
}
}
}
fun getHealthCheckH5Url(baseUrlType: BaseUrlType = BaseUrlType.PRODUCT): String {
return when (baseUrlType) {
BaseUrlType.DEBUG, BaseUrlType.TEST-> {
TEST_HOME_HEALTH_CHECK_H5
}
BaseUrlType.PRODUCT -> {
PRODUCT_HOME_HEALTH_CHECK_H5
}
}
}
fun getWeightManagementH5Url(baseUrlType: BaseUrlType = BaseUrlType.PRODUCT): String {
return when (baseUrlType) {
BaseUrlType.DEBUG, BaseUrlType.TEST-> {
PRODUCT_HOME_WEIGHT_MANAGEMENT_H5
}
BaseUrlType.PRODUCT -> {
PRODUCT_HOME_WEIGHT_MANAGEMENT_H5
}
}
}
fun getHealthFileH5Url(baseUrlType: BaseUrlType = BaseUrlType.PRODUCT): String {
return when (baseUrlType) {
BaseUrlType.DEBUG, BaseUrlType.TEST-> {
PRODUCT_HOME_HEALTH_FILE_H5
}
BaseUrlType.PRODUCT -> {
PRODUCT_HOME_HEALTH_FILE_H5
}
}
}
/**
* 健康资讯 H5 页面基础地址
*/
fun getHealthNewsH5BaseUrl(): String {
return getH5BaseXjUrl() + "healthnewsweb/"
}
/**
* 健康知识 H5 页面地址
*/
fun getHealthKnowledgeH5Url(): String {
return getHealthNewsH5BaseUrl() + "healthKnow.html"
}
/**
* 健康咨询 H5 页面地址
*/
fun getHealthConsultationH5Url(): String {
return getHealthNewsH5BaseUrl() + "healthNews.html"
}
/**
* 健康资讯详情 H5 页面地址
*/
fun getHealthNewsDetailH5Url(): String {
return getHealthNewsH5BaseUrl() + "healthNewsDetail.html"
}
/**
* 健康资讯搜索 H5 页面地址
*/
fun getHealthNewsSearchH5Url(): String {
return getHealthNewsH5BaseUrl() + "healthSecrch.html"
}
/**
* 方便后续加一些其他逻辑
*/
@@ -78,6 +204,13 @@ object UrlConfig {
return getBaseUrl(baseUrlType)
}
/**
* 手机银行
*/
fun getDefaultBankBaseUrl(): String {
return getBankBaseUrl(baseUrlType)
}
/**
* 方便在APP运行之后切换环境所以用 get获取
*/
@@ -111,67 +244,26 @@ object UrlConfig {
}
private fun urlReplace(): String {
return getDefaultBaseUrl().replace("http://", "")
.replace("https://", "")
.replace("/", "")
/**
* 获取评估报告模块地址
* @param type 0,健康风险 1,心理评估
*/
}
// fun getEvaluationReportUrl(type: Int): String {
// fun getKnowledgeDetailUrl(@UrlH5RouteUtils.RouteType routeType: String, map: MutableMap<String, Any?> = mutableMapOf()):String{
//// return "http://192.168.1.22:9000/#/rich-text?id=sacsacasc&value=123132132"
// var params=""
// map.mapKeys {
// params += "&${it.key}=${it.value}"
// }
// var httpUrl = getDefaultBaseUrl().toHttpUrl()
// return getH5Url() +
// "/?scheme=" + httpUrl.scheme +
// return getH5Url() + "/" +routeType+
// "?scheme=" + httpUrl.scheme +
// "&host=" + httpUrl.host +
// "&mobile=0" +
// "&tokenF=" + DataStoreManager.getToken() +
// "&type=" + type
// }
fun getKnowledgeDetailUrl(@UrlH5RouteUtils.RouteType routeType: String, map: MutableMap<String, Any?> = mutableMapOf()):String{
// return "http://192.168.1.22:9000/#/rich-text?id=sacsacasc&value=123132132"
var params=""
map.mapKeys {
params += "&${it.key}=${it.value}"
}
var httpUrl = getDefaultBaseUrl().toHttpUrl()
return getH5Url() + "/" +routeType+
"?scheme=" + httpUrl.scheme +
"&host=" + httpUrl.host +
"&tokenF=" + DataStoreManager.getToken() +
params
}
/**
* 获取健康干预-膳食 接口地址
*/
// fun getInterventionFoodUrl():String {
// return when (baseUrlType) {
// BaseUrlType.DEBUG,BaseUrlType.TEST -> {
// "https://vip.shuziweidao.com"
// }
// BaseUrlType.PRODUCT -> {
// "https://yyjk.shuziweidao.com/"
// }
// }
// params
// }
/**
* H5访问地址
*/
fun getH5PdfUrl(): String {
// https://jkgy.wcrcnet.cn/static/mobile/indexSon?
// // scheme=http
// // s&host=jkgy.wcrcnet.cn
// // &hostAfter=gateway
// // &pdfUrl=temp/20240530/AED%E4%BD%BF%E7%94%A8.pdf_1717056401896.pdf
val baseUrl = getH5BaseUrl(baseUrlType) + "/static/mobile/indexSon?"
var httpUrl = getDefaultBaseUrl().toHttpUrl()
return baseUrl +
"scheme=" + httpUrl.scheme +
"&host=" + httpUrl.host +
"&title=" + "文件详情" +
"&pdfUrl="
}
}
@@ -793,16 +793,16 @@ fun startEmergencySeekDoctorDetailsActivity(context: Context, id: String, sessio
//}
//
/**
* Web页面 source == 1 绿色主题 darkStyle=false 白色顶部
* Web页面 darkStyle=false 白色顶部
*/
fun Context.startWebActivity(url: String, scale: Boolean = false,myTitle:String="", source: Int = 0,darkStyle: Boolean = true,isFull:Boolean=false) {
fun Context.startWebActivity(url: String, scale: Boolean = false,myTitle:String="",darkStyle: Boolean = true,isFull:Boolean=false,hideStatus:Boolean=false) {
val bundle = Bundle()
bundle.putString("url", url)
bundle.putBoolean("Scale", scale)
bundle.putInt("source", source)
bundle.putString("title", myTitle)
bundle.putBoolean("darkStyle", darkStyle)//主题
bundle.putBoolean("isFull", isFull)//是否全屏
bundle.putBoolean("hideStatus", hideStatus)//是否隐藏状态栏
startActivity(this, bundle = bundle, targetClass = WebActivity::class.java)
}
///**
@@ -590,6 +590,7 @@ fun TabLayout.attachViewPager(
fun getTabStyle(i: Int): Int {
return when (i) {
1 -> R.layout.item_tablayout_group_title
2 -> R.layout.item_tablayout_group_index_theme//有下标线的绿色
else -> R.layout.item_tablayout_group_title
}
}
@@ -0,0 +1,169 @@
package com.xjjk.healthyclients.ui.activity
import android.graphics.Color
import android.os.Bundle
import android.view.View
import androidx.core.text.isDigitsOnly
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.base.BaseVMBActivity
import com.xjjk.healthyclients.bean.home.ProposalBean
import com.xjjk.healthyclients.databinding.ActivityProposalBinding
import com.xjjk.healthyclients.ui.viewmodel.ProposalViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
/**
* 一人一案(健康档案)页面
*/
class ProposalActivity :
BaseVMBActivity<ProposalViewModel, ActivityProposalBinding>(R.layout.activity_proposal) {
override fun initView(savedInstanceState: Bundle?) {
val stringExtra = intent.getStringExtra("title")
mBinding.toolbarLay.title = stringExtra ?: "一人一案"
}
override fun initData() {
// 加载健康档案数据
}
override fun createObserve() {
super.createObserve()
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
mViewModel.proposalInfo.collectLatest {
setProposalInfo(it)
}
}
}
}
/**
* 手机号中间4位脱敏
*/
fun String?.maskPhone(): String {
val phone = this ?: return "--"
// 判断是否标准11位
return if (phone.length == 11) {
phone.replaceRange(3, 7, "****")
} else {
phone
}
}
private fun setProposalInfo(proposalBean: ProposalBean?) {
proposalBean?.let {
mBinding.llDocumentBuild.visibility = View.VISIBLE
mBinding.llMostCare.visibility = View.VISIBLE
// 患者信息
mBinding.tvPatientName.text = it.name
mBinding.tvPatientGenderAge.text = formatNullString(it.sexCode) + " · " + it.age + ""
//mBinding.tvDocumentStatus.text = it.careStatus
mBinding.tvPatientCompany.text = formatNullString(it.workUnit)
mBinding.tvIdNumber.text = formatNullString(it.idNo)
mBinding.tvPhone.text = formatNullString(it.phone).maskPhone()
// 关爱管理信息
//mBinding.tvLevel.text = it.dataLevel.toString()
mBinding.tvLiaisonName.text = formatNullString(it.careLiaisonName)
mBinding.tvNegativeList.text = formatNullString(it.negativeList)
// 档案与标签
if (it.flagFat == 1) {
mBinding.llFatTag.visibility = View.VISIBLE
}
val weight = formatNullString(it.weight)
val formatWeight = weight.toDoubleOrNull()?.let {
String.format("%.2f", it)
} ?: weight
val standardWeight = formatNullString(it.targetWeight)
val targetWeight = standardWeight.toDoubleOrNull()?.let {
String.format("%.2f", it)
} ?: standardWeight
mBinding.tvWeight.text = formatWeight
mBinding.tvHeight.text = formatNullString(it.height)
mBinding.tvBmi.text = formatNullString(it.bmiCurrent)
mBinding.tvStandardWeight.text = targetWeight
// 心血管健康
if (it.hypertension == 1) {
mBinding.llHighPressureTag.visibility = View.VISIBLE
}
mBinding.tvSystolicAvg.text = formatNullString(it.systolicPressureAvg)
mBinding.tvSystolicTarget.text = formatNullString(it.systolicPressureTarget)
mBinding.tvSystolic.text = formatNullString(it.systolicPressureCurrent)
if (it.systolicPressureTarget?.toDoubleOrNull() !=null && it.systolicPressureCurrent?.toDoubleOrNull() !=null
&& it.systolicPressureTarget.toDouble() < it.systolicPressureCurrent.toDouble()
) {
mBinding.tvSystolic.setTextColor(getColor(R.color.red_f2))
}
mBinding.tvDiastolicAvg.text = formatNullString(it.diastolicPressureAvg)
mBinding.tvDiastolicTarget.text = formatNullString(it.diastolicPressureTarget)
mBinding.tvDiastolic.text = formatNullString(it.diastolicPressureCurrent)
// 代谢指标
// 心血管健康
if (it.diabetes == 1) {
mBinding.llDiabetesTag.visibility = View.VISIBLE
}
mBinding.tvFbsValue.text = formatNullString(it.glu)
mBinding.tvFbsTarget.text = formatNullString(it.bloodSugarTarget)
mBinding.tvFbsCurrent.text = formatNullString(it.bloodSugarCurrent)
// 血脂四项
mBinding.tvTcValue.text = it.tc
mBinding.tvTcTarget.text = it.tcTarget
mBinding.tvTcCurrent.text = it.tc
mBinding.tvTgValue.text = it.tg
mBinding.tvTgTarget.text = it.tgTarget
mBinding.tvTgCurrent.text = it.tg
mBinding.tvLdlValue.text = it.ldl
mBinding.tvLdlTarget.text = it.ldlTarget
mBinding.tvLdlCurrent.text = it.ldl
// 肾脏与骨骼代谢
mBinding.tvUaValue.text = it.uricAcid
mBinding.tvUaTarget.text = it.uricAcidTarget
mBinding.tvUaCurrent.text = it.uricAcid
// 特殊代谢指标
mBinding.tvHcyValue.text = it.homocysteine
mBinding.tvHcyTarget.text = it.homocysteineTarget
mBinding.tvHcyCurrent.text = it.homocysteine
// 医疗意见与记录
mBinding.tvCabinOpinion.text = it.cabinOpinion ?: "暂无小屋意见"
mBinding.tvCabinTime.text = it.cabinOpinionTime
mBinding.tvCabinReporter.text = it.cabinOpinionUserName
mBinding.tvExpertOpinion.text = it.expertOpinion ?: "暂无专家医生意见"
mBinding.tvExpertOpinionTime.text = it.expertOpinionTime
mBinding.tvExpertOpinionUserName.text = it.expertOpinionUserName
}
}
fun formatNullString(input: String?): String {
return input?.ifEmpty {
"--"
} ?: "--"
}
override fun bindEvent() {
// 绑定事件
}
override fun processClick(paramView: View?) {
// 处理点击事件
}
override fun transparentStatusBar(): Boolean {
return false
}
override fun statusBarDarkFont(): Boolean {
return true
}
}
@@ -14,6 +14,11 @@ import com.amap.api.maps.model.CameraPosition
import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.MarkerOptions
import com.amap.api.maps.model.MyLocationStyle
import com.amap.api.services.core.LatLonPoint
import com.amap.api.services.geocoder.GeocodeResult
import com.amap.api.services.geocoder.GeocodeSearch
import com.amap.api.services.geocoder.RegeocodeQuery
import com.amap.api.services.geocoder.RegeocodeResult
import com.google.gson.Gson
import com.permissionx.guolindev.PermissionX
import com.tencent.qcloud.tuicore.util.ToastUtil
@@ -38,6 +43,7 @@ class SelectLocationActivity : BaseVMBActivity<TestViewModel, ActivitySelectLoca
var aMap: AMap? = null
var mCurrentLat: Double = 0.0
var mCurrentLon: Double = 0.0
var desc: String = ""
var mPermissionList= arrayListOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION)
@@ -72,6 +78,34 @@ class SelectLocationActivity : BaseVMBActivity<TestViewModel, ActivitySelectLoca
override fun initData() {
}
private fun initGeocoderSearch() {
val geocodeSearch = GeocodeSearch(this@SelectLocationActivity)
val query = RegeocodeQuery(LatLonPoint(mCurrentLat, mCurrentLon), 200f, GeocodeSearch.AMAP)
geocodeSearch.getFromLocationAsyn(query)
geocodeSearch.setOnGeocodeSearchListener(object : GeocodeSearch.OnGeocodeSearchListener {
override fun onRegeocodeSearched(
regeocodeResult: RegeocodeResult?,
resultID: Int
) {
if (regeocodeResult != null) {
val regeocodeAddress = regeocodeResult.regeocodeAddress
desc = regeocodeAddress.getFormatAddress()
}
}
override fun onGeocodeSearched(
geocodeResult: GeocodeResult?,
resultID: Int
) {
if (geocodeResult != null) {
val geocodeAddressList = geocodeResult.geocodeAddressList
}
}
})
}
private fun setUpMap() {
// 自定义系统定位小蓝点
val myLocationStyle = MyLocationStyle()
@@ -91,6 +125,9 @@ class SelectLocationActivity : BaseVMBActivity<TestViewModel, ActivitySelectLoca
aMap?.setOnMyLocationChangeListener {
mCurrentLat = it.latitude
mCurrentLon = it.longitude
initGeocoderSearch()
val markerOption = MarkerOptions()
markerOption.position(LatLng(mCurrentLat, mCurrentLon))
aMap?.moveCamera(CameraUpdateFactory.newLatLng(LatLng(mCurrentLat, mCurrentLon)))
@@ -108,6 +145,8 @@ class SelectLocationActivity : BaseVMBActivity<TestViewModel, ActivitySelectLoca
marker?.position = cameraPosition.target
mCurrentLat = cameraPosition.target.latitude
mCurrentLon = cameraPosition.target.longitude
initGeocoderSearch()
}
}
@@ -144,6 +183,7 @@ class SelectLocationActivity : BaseVMBActivity<TestViewModel, ActivitySelectLoca
var locationMessageBean = LocationMessageBean()
locationMessageBean.longitude = mCurrentLon
locationMessageBean.latitude = mCurrentLat
locationMessageBean.desc = desc
intent.putExtra(ChatLayoutSetting.KEY_CUSTOM_MESSAGE, locationMessageBean.toJson())
setResult(ChatLayoutSetting.RESULT_CODE_LOCATION, intent)
finish()
@@ -3,7 +3,6 @@ package com.xjjk.healthyclients.ui.activity
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Bundle
import android.text.TextUtils
import android.view.View
@@ -66,26 +65,27 @@ class WebActivity : BaseVMBActivity<WebViewModel, ActivityWebBinding>(R.layout.a
.ready()
.go(getUrl())
mAgentWeb.agentWebSettings.webSettings.javaScriptEnabled = true
mAgentWeb.agentWebSettings.webSettings.cacheMode = WebSettings.LOAD_NO_CACHE
mAgentWeb.jsInterfaceHolder.addJavaObject("android", androidInterface)
}
override fun dataBindingFinish() {
val source = intent.getIntExtra("source", 0)
customTitle = intent.getStringExtra("title")
if (source == 1) {//深绿色
mBinding.toolbarLay.rlTitleLay.setBackgroundColor(Color.parseColor("#117474"))
}
mBinding.toolbarLay.darkStyle = intent.getBooleanExtra("darkStyle", true)
if (intent.getBooleanExtra("isFull", false)) {
mBinding.toolbarLay.root.visibility = View.GONE
if (intent.getBooleanExtra("hideStatus", false)) {
mBinding.statusView.visibility = View.GONE
setTransparentStatusBar(true,false)
} else {
mBinding.statusView.visibility = View.VISIBLE
val statusBarHeight = StatusbarUtil.getStatusBarHeight(this)
val layoutParams = mBinding.statusView.layoutParams
layoutParams.height = statusBarHeight
mBinding.statusView.layoutParams = layoutParams
}
}
}
override fun initData() {
mIsScale = intent.getBooleanExtra("Scale", false)
@@ -110,11 +110,13 @@ class WebActivity : BaseVMBActivity<WebViewModel, ActivityWebBinding>(R.layout.a
override fun onBackEvent() {
if (!mAgentWeb.back()) {
if (jsAffirmBack.isNullOrEmpty()) {
mAgentWeb.webCreator.webView?.stopLoading()
super.onBackEvent()
} else {
mAgentWeb.jsAccessEntrace.quickCallJs("interceptBack",
{ value ->
if ("1" != value) {
mAgentWeb.webCreator.webView?.stopLoading()
finish()
}
})
@@ -273,14 +275,10 @@ class WebActivity : BaseVMBActivity<WebViewModel, ActivityWebBinding>(R.layout.a
}
}
}
var isRefresh = false
override fun onResume() {
mAgentWeb.webLifeCycle.onResume()
if (getUrl().contains("motionClock")&&isRefresh) {//动打卡链接,同步微信步数再次刷新界面
mAgentWeb.urlLoader.reload()
}
super.onResume()
}
@@ -293,7 +291,6 @@ class WebActivity : BaseVMBActivity<WebViewModel, ActivityWebBinding>(R.layout.a
override fun onPause() {
mAgentWeb.webLifeCycle.onPause()
isRefresh = true
super.onPause()
}
@@ -1,8 +1,13 @@
package com.xjjk.healthyclients.ui.viewmodel
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.xjjk.healthyclients.base.viewmodel.BaseViewModel
import com.xjjk.healthyclients.bean.emergency.EmergencyGroupInfo
import com.xjjk.healthyclients.bean.emergency.GroupInfo
import com.xjjk.healthyclients.bean.emergency.LocationResourceBean
import com.xjjk.healthyclients.data.bean.PageBean
import com.xjjk.healthyclients.data.repository.EmergencyRepository
import com.xjjk.healthyclients.superfuntion.handleRequest
import com.xjjk.healthyclients.superfuntion.launch
@@ -42,6 +47,35 @@ class EmergencyViewModel : BaseViewModel() {
)
}
fun isLiaisonUser(successCall: (Boolean) -> Unit = {}){
launch(
{
handleRequest(
EmergencyRepository.isLiaisonUser(),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
}
)
}
fun getParticipatedOrders(successCall: (PageBean<GroupInfo>) -> Unit = {}){
launch(
{
handleRequest(
EmergencyRepository.getParticipatedOrders(),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
}
)
}
fun getEmergencyCall(
callType: String,
resourceId: String,
@@ -99,6 +133,101 @@ class EmergencyViewModel : BaseViewModel() {
})
})
}
fun getParamedicGroupInfo(
groupName: String, longitude: Double,
latitude: Double, successCall: (EmergencyGroupInfo) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.getParamedicGroupInfo(groupName, longitude, latitude),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun searchExpert(
realname: String, centerId: String, excludeSessionId: String, pageNo: Int, pageSize: Int, successCall: (List<ExpertBean>) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.searchExpert(realname, centerId, excludeSessionId, pageNo, pageSize),
successBlock = {
it.result?.records?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun searchEmployee(
realname: String, orgCode: String, workNo: String, phone: String, excludeSessionId: String, pageNo: Int, pageSize: Int,
successCall: (List<EmployeeBean>) -> Unit = {}) {
launch({
handleRequest(
EmergencyRepository.searchEmployee(realname, orgCode, workNo, phone, excludeSessionId, pageNo, pageSize),
successBlock = {
it.result?.records?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun getActualGroupMemberList(sessionId: String, successCall: (List<GroupMemberBean>) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.getActualGroupMemberList(sessionId),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
/**
* 邀请成员加入群组。
*
* @param groupId 群组 ID
* @param memberList 成员 userId 列表
* @param memberType 成员类型
* @param successCall 结果回调:true 表示服务器写入成功,false 表示失败(网络异常、业务错误、result 为空或 false
* 回调在协程上下文触发,调用方需自行切回主线程
*/
fun addGroupUser(groupId: String, memberList: List<String>,
memberType: Int, successCall: (Boolean) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.addGroupUser(groupId, memberList, memberType),
successBlock = {
// result 为空或 false 都视为失败,便于调用方串行后续动作(如刷新列表)
successCall.invoke(it.result == true)
},
errorBlock = {
successCall.invoke(false)
false
})
})
}
fun removeGroupUser(groupId: String, memberList: List<String>, successCall: (Boolean) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.removeGroupUser(groupId, memberList),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
//
// /**
// * 附近医疗点
@@ -28,6 +28,7 @@ class HomeViewModel : BaseViewModel() {
//手表
var watchInfo = MutableSharedFlow<WatchInfoBean?>()
var watchAllData = MutableSharedFlow<MutableList<HomeWatchAllDataBean>?>()
var watchBindStatus = MutableSharedFlow<String?>()
var isRefreshing = MutableStateFlow(false)
@@ -177,6 +178,29 @@ class HomeViewModel : BaseViewModel() {
}
)
}
fun getBindDevice(str: String?) {
launch(
{
handleRequest(
HomeRepository.getBindDevice(str),
successBlock = {
watchBindStatus.emit(it.result)
})
}
)
}
fun getUnbundleDevice(str: String?) {
launch(
{
handleRequest(
HomeRepository.getUnbundleDevice(str),
successBlock = {
watchBindStatus.emit(it.result)
})
}
)
}
}
@@ -0,0 +1,32 @@
package com.xjjk.healthyclients.ui.viewmodel
import com.xjjk.healthyclients.base.viewmodel.BaseViewModel
import com.xjjk.healthyclients.bean.home.ProposalBean
import com.xjjk.healthyclients.data.repository.EmergencyRepository
import com.xjjk.healthyclients.superfuntion.handleRequest
import com.xjjk.healthyclients.superfuntion.launch
import kotlinx.coroutines.flow.MutableStateFlow
/**
* 一人一案(健康档案)ViewModel
*/
class ProposalViewModel : BaseViewModel() {
var proposalInfo = MutableStateFlow<ProposalBean?>(null)
override fun init() {
// 初始化逻辑
getProposalInfo()
}
fun getProposalInfo() {
launch({
handleRequest(
EmergencyRepository.getProposalInfo(),
successBlock = {
it.result?.let { it1 ->
proposalInfo.emit(it1)
}
})
})
}
}
@@ -56,7 +56,6 @@ class AndroidInterface(val agentWeb: AgentWeb, val context: Activity) {
@JavascriptInterface
fun finishActivity() {
context.runOnUiThread {
EventBus.getDefault().post(WebReloadEvent())
context.finish()
}
}
@@ -11,6 +11,8 @@ object ConstantUtils {
var mCurrentLon = 87.627704
var mNutritionMonitor = false
var mIsCloseDialog = false
val APP_ID: String = "wxb33cd60beda0a212"
var mPrivacy = "本平台由西安联链科技有限公司开发和运维,平台深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。我们致力于维持您对我们的信任,恪守以下原则,保护您的个人信息:权责一致原则、目的明确原则、选择同意原则、最少够用原则、确保安全原则、主体参与原则、公开透明原则等。同时,我们承诺将采取相应的安全保护措施来保护您的个人信息。请在使用平台服务前,仔细阅读并了解本隐私权政策,我们将按照本政策收集、处理及披露您的信息。\n" +
"本政策将帮助您了解以下内容:\n" +
"1、定义\n" +
@@ -8,7 +8,7 @@ object IMInputActionSettingUtils {
/**
* 应急就医IM功能配置
*/
fun createEmergencySetting(){
fun createEmergencySetting(firstAid: Boolean = false){//是否为急救员
var inputActionSetting = InputActionSetting.createInstance()
inputActionSetting.packageName = BuildConfig.APPLICATION_ID
inputActionSetting.CurrentResourceHost = UrlConfig.IMAGE_BASE_URL
@@ -20,6 +20,7 @@ object IMInputActionSettingUtils {
inputActionSetting.isDisableMedicalExaminationReport = true
inputActionSetting.isDisableSendMessage = false
inputActionSetting.isDisableFinishSession = false
inputActionSetting.isFirstAid = firstAid
}
/**
* 图文咨询IM功能配置
@@ -0,0 +1,68 @@
package com.xjjk.healthyclients.utils
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
/**
* 签名工具类
* 用于生成 HMAC-SHA256 签名,支持 API 请求的身份验证
*/
object SignatureUtils {
private const val ALGORITHM = "HmacSHA256"
private const val HEX_CHARS = "0123456789abcdef"
/**
* 生成签名
*
* @param timestamp 时间戳(秒级)
* @param bodyString 请求体原始 JSON 字符串
* @param secretKey 密钥
* @return 64 位小写十六进制签名字符串
*/
fun generateSignature(timestamp: Long, bodyString: String, secretKey: String): String {
// 构造待签名字符串:timestamp + bodyString
val stringToSign = "$timestamp$bodyString"
// 使用 HMAC-SHA256 算法加密
val mac = Mac.getInstance(ALGORITHM)
val secretKeySpec = SecretKeySpec(secretKey.toByteArray(), ALGORITHM)
mac.init(secretKeySpec)
// 获取签名字节数组
val signatureBytes = mac.doFinal(stringToSign.toByteArray())
// 转换为十六进制小写字符串
return bytesToHex(signatureBytes)
}
/**
* 将字节数组转换为十六进制小写字符串
*
* @param bytes 字节数组
* @return 十六进制小写字符串
*/
private fun bytesToHex(bytes: ByteArray): String {
val result = StringBuilder(bytes.size * 2)
for (byte in bytes) {
val octet = byte.toInt()
result.append(HEX_CHARS[octet shr 4 and 0xF])
result.append(HEX_CHARS[octet and 0xF])
}
return result.toString()
}
/**
* 验证签名是否正确
*
* @param timestamp 时间戳
* @param bodyString 请求体原始 JSON 字符串
* @param secretKey 密钥
* @param signature 待验证的签名
* @return true 签名正确,false 签名错误
*/
fun verifySignature(timestamp: Long, bodyString: String, secretKey: String, signature: String): Boolean {
val generatedSignature = generateSignature(timestamp, bodyString, secretKey)
return generatedSignature.equals(signature, ignoreCase = false)
}
}
@@ -1,190 +0,0 @@
package com.xjjk.healthyclients.utils
import androidx.annotation.StringDef
object UrlH5RouteUtils {
/**
* 知识库文章详情
*/
const val RICH_TEXT = "knowledge-page"
/**
* 岗位健康详情页
*/
const val JOBDETAILS = "jobDetails"
/**
* 知识库健-康答题
*/
const val HEALTH_QUESTION = "mergeQuestion/physical-questions"
/**
* 知识库答题记录
*/
const val ANSWER_RECORDSLIST = "answer-recordsList"
/**
* 膳食-慢病预警测评结果
*/
const val DISEASE_ASSESSMENT_RESULT = "disease-assessment-result"
/**
* 膳食-体重管理
*/
const val FOOD_OVERWEIGHT_INTERVENTION = "overweightIntervention"
/**
* 运动-运动平台
*/
const val SPORT_PLATFORM = "sports-platform"
/**
* 运动-运动排名
*/
const val SPORT_RANKING = "sport-ranking"
/**
* 步数
*/
const val STEP_STATISTICS = "step-statistics"
/**
* 心理-心理评估
*/
const val MENTAL_PSYCHOLOGICAL_ASSESSMENT = "assessment-home"
/**
* 心理-心理评估-基础信息basic-information
*/
const val MENTAL_PSYCHOLOGICAL_ASSESSMENT_BASIC_INFORMATION = "basic-information"
/**
* 心理-心理评估-测评模式self-evaluation
*/
const val MENTAL_PSYCHOLOGICAL_ASSESSMENT_SELF_EVALUATION = "self-evaluation"
/**
* 心理-知识答题-答题模式
*/
const val MENTAL_KNOWLEDGE_CHOOSE_TOPIC = "choose-topic"
/**
* 心理-心理评估-测评历史reportHistory
*/
const val MENTAL_PSYCHOLOGICAL_ASSESSMENT_REPORTHISTORY = "reportHistory"
/**
* 心理-知识答题-答题历史
*/
const val MENTAL_KNOWLEDGE_ANSWER_HISTORY = "answer-history"
/**
* 心理-心理评估-单位组织task
*/
const val MENTAL_PSYCHOLOGICAL_ASSESSMENT_TASK = "task"
/**
* 心理-知识答题-单位组织task
*/
const val MENTAL_KNOWLEDGE_TASK_TOPIC = "task-topic"
/**
* 心理-规律起居
*/
const val MENTAL_REGULAR_LIVING = "livingIndependent"
/**
* 糖尿病-血糖监测
*/
const val DIABETES_BLOOD_GLUCOSE_MONITORING = "bloodGlucose"
/**
* 糖尿病-信号预警
*/
const val DIABETES_SIGNAL_WARNING = "health"
/**
* 癌症-病因溯源
*/
const val CANCER_ETIOLOGICAL_TRACING = "etiological"
/**
* 癌症-免疫预警
*/
const val CANCER_IMMUNE_EARLY_WARNING = "immuneWarning"
/**
* 知识-体检可视化
*/
const val PHYSICAL_EXAMINATION_HOME = "physicalExaminationIndex"
/**
* 心血管-吃动平衡
*/
const val CARDIOVASCULAR_EATING_BALANCE = "healthIndependentC"
/**
* 随访记录详情
*/
const val INTERVENTION_FOLLOW = "follow"
/**
* 就诊记录详情
*/
const val INTERVENTION_TREATMENTDETAIL = "treatmentDetail"
/**
* 急救培训开始答题
*/
const val HEALTH_ANSWER = "mergeQuestion/physical-answer"
/**
* 癌症-信号预判
*/
const val CANCER_SIGNAL_PREDICTION = "mergeQuestion/home-prediction"
/**
* 癌症-免疫预警
*/
const val INTERVENTION_IMMUNE_WARNING = "mergeQuestion/immune-warning"
/**
* 病因溯源填写问卷
*/
const val IMAGE_TRACEABILITY = "mergeQuestion/image-traceability"
/**
* 急救培训答题回顾
*/
const val HEALTH_ANSWER_REVIEW = "mergeQuestion/physical-review"
/**
* 吃动平衡问卷调查
*/
const val Eating_ANSWER_REVIEW = "mergeQuestion/eating-balance"
/**
* 心理知识答题-知识详情
*/
const val KNOWLEDGE_DETAIL = "knowledge-detail"
/**
* 心血管-防范心梗问卷
*/
const val HEART_ATTACK = "heart-attack"
/**
* 心血管-风险详情
*/
const val HEART_FACTORS = "heart-factors"
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD, AnnotationTarget.FUNCTION)
@MustBeDocumented
@StringDef(
RICH_TEXT,
JOBDETAILS, HEALTH_QUESTION, ANSWER_RECORDSLIST, DISEASE_ASSESSMENT_RESULT,
SPORT_RANKING, STEP_STATISTICS, MENTAL_PSYCHOLOGICAL_ASSESSMENT, SPORT_PLATFORM, MENTAL_REGULAR_LIVING,
DIABETES_BLOOD_GLUCOSE_MONITORING, DIABETES_SIGNAL_WARNING, CANCER_ETIOLOGICAL_TRACING,
CANCER_IMMUNE_EARLY_WARNING, FOOD_OVERWEIGHT_INTERVENTION, PHYSICAL_EXAMINATION_HOME,
CARDIOVASCULAR_EATING_BALANCE,
INTERVENTION_FOLLOW,
INTERVENTION_TREATMENTDETAIL,
HEALTH_ANSWER,
CANCER_SIGNAL_PREDICTION,
INTERVENTION_IMMUNE_WARNING,
IMAGE_TRACEABILITY,
HEALTH_ANSWER_REVIEW,
Eating_ANSWER_REVIEW
,
INTERVENTION_TREATMENTDETAIL,
MENTAL_PSYCHOLOGICAL_ASSESSMENT_BASIC_INFORMATION,
MENTAL_PSYCHOLOGICAL_ASSESSMENT_SELF_EVALUATION,
MENTAL_PSYCHOLOGICAL_ASSESSMENT_REPORTHISTORY,
MENTAL_PSYCHOLOGICAL_ASSESSMENT_TASK,
KNOWLEDGE_DETAIL,MENTAL_KNOWLEDGE_CHOOSE_TOPIC,MENTAL_KNOWLEDGE_ANSWER_HISTORY,MENTAL_KNOWLEDGE_TASK_TOPIC,HEART_ATTACK,HEART_FACTORS
)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
annotation class RouteType
}
@@ -4,7 +4,6 @@ import android.app.AlertDialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
@@ -21,17 +20,11 @@ import com.xjjk.healthyclients.databinding.DialogAppraiseBinding
class AppraiseDialog constructor(
context: Context
) : AlertDialog(context) {
lateinit var mOnSubmitClickListener: OnSubmitClickListener
// 提交点击监听器
var mOnSubmitClickListener: OnSubmitClickListener?=null
// 数据绑定对象
lateinit var binding: DialogAppraiseBinding
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
create()
} else {
onCreate(null)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.inflate(
@@ -47,19 +40,15 @@ class AppraiseDialog constructor(
);//设置对话框大小
window?.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
binding.btnSubmit.setOnClickListener {
if (getRating()==0F) {
if (getRating() == 0F) {
CustomToast.makeText(context, "您还没有选择星级评分", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (this::mOnSubmitClickListener.isInitialized) {
mOnSubmitClickListener?.onSubmitClick(this@AppraiseDialog)
}
this.cancel()
}
binding.btnClose.setOnClickListener {
if (this::mOnSubmitClickListener.isInitialized) {
mOnSubmitClickListener?.onCancelClick(this@AppraiseDialog)
}
this.cancel()
}
@@ -70,11 +59,26 @@ class AppraiseDialog constructor(
return this
}
/**
* 评分提交回调接口
*/
interface OnSubmitClickListener {
/**
* 提交按钮点击回调
*/
fun onSubmitClick(viewDialog: AppraiseDialog)
/**
* 关闭按钮点击回调
*/
fun onCancelClick(viewDialog: AppraiseDialog){}
}
/**
* 设置对话框标题
* @param title 标题文本
* @param titleSize 标题字体大小,默认 18f
* @return 返回当前对象,支持链式调用
*/
fun setDialogTitle(title: String, titleSize: Float = 18f): AppraiseDialog {
binding.tvTitle.visibility = View.VISIBLE
binding.tvTitle.text = title
@@ -82,35 +86,67 @@ class AppraiseDialog constructor(
return this
}
/**
* 设置评价内容输入框提示文本
* @param text 提示文本
* @param textSize 字体大小,默认 13f
* @return 返回当前对象,支持链式调用
*/
fun setContentHint(text: String, textSize: Float = 13f): AppraiseDialog {
binding.etAppraise.hint = text
binding.etAppraise.textSize = textSize
return this
}
/**
* 设置提交按钮文本
* @param text 按钮文本
* @param btnTextSize 字体大小,默认 18f
* @return 返回当前对象,支持链式调用
*/
fun setBtnText(text: String, btnTextSize: Float = 18f): AppraiseDialog {
binding.btnSubmit.text = text
binding.btnSubmit.textSize = btnTextSize
return this
}
/**
* 设置对话框是否可取消
* @param flag true 可取消,false 不可取消
* @return 返回当前对象,支持链式调用
*/
fun setDialogCancelable(flag: Boolean): AppraiseDialog {
setCancelable(flag)
return this
}
/**
* 获取用户输入的评价内容
* @return 评价文本
*/
fun getAppraiseContext(): String {
return binding.etAppraise.text.trim().toString()
}
/**
* 获取评分条控件
* @return RatingBar 对象
*/
fun getRatingBar(): RatingBar {
return binding.ratingBar
}
fun getRating(): Float {
return getRatingBar().rating
}
/**
* 获取匿名状态
* 获取用户选择的星级评分
* @return 评分值(0-5
*/
fun getRating(): Float {
return binding.ratingBar.rating
}
/**
* 获取匿名评价状态
* @return true 为匿名,false 为非匿名
*/
fun getAnonymityStatus(): Boolean {
return binding.cbAnonymity.isChecked
@@ -0,0 +1,73 @@
package com.xjjk.healthyclients.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.RelativeLayout
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.adapter.HealthConsultationAdapter
import com.xjjk.healthyclients.bean.home.ArticleBean
import com.xjjk.healthyclients.databinding.LayoutHealthConsultationBinding
/**
* 健康咨询 View 组件
* 头部标题 + 列表(左侧标题+收藏分享信息 / 右侧图片)
*/
class HealthConsultationView(context: Context?, attrs: AttributeSet?) :
RelativeLayout(context, attrs, 0) {
var mContext: Context? = context
var mBinding: LayoutHealthConsultationBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.layout_health_consultation, this, true
)
private val mAdapter = HealthConsultationAdapter()
private var mOnItemClickListener: ((ArticleBean) -> Unit)? = null
private var mOnMoreClickListener: (() -> Unit)? = null
init {
mBinding.rvList.layoutManager = LinearLayoutManager(mContext)
mBinding.rvList.adapter = mAdapter
// 列表项点击
mAdapter.setOnItemClickListener { _, _, position ->
mAdapter.getItem(position)?.let { mOnItemClickListener?.invoke(it) }
}
// 头部更多按钮点击
mBinding.ivMore.setOnClickListener {
mOnMoreClickListener?.invoke()
}
}
/** 设置文章列表数据(替换),最多取前 2 条 */
fun setArticles(articles: List<ArticleBean>?) {
if (articles.isNullOrEmpty()) {
mBinding.rvList.visibility = GONE
mBinding.tvEmpty.visibility = VISIBLE
return
} else {
mBinding.rvList.visibility = VISIBLE
mBinding.tvEmpty.visibility = GONE
}
val topTwo = articles.take(2).toMutableList()
mAdapter.setNewInstance(topTwo)
}
/** 追加文章列表数据(分页加载) */
fun addArticles(articles: List<ArticleBean>) {
mAdapter.addData(articles)
}
fun setOnItemClickListener(listener: (ArticleBean) -> Unit) {
mOnItemClickListener = listener
}
fun setOnMoreClickListener(listener: () -> Unit) {
mOnMoreClickListener = listener
}
}
@@ -0,0 +1,151 @@
package com.xjjk.healthyclients.view
import android.content.Context
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.RelativeLayout
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.healthyclients.view.CustomToast
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.adapter.HealthKnowledgeAdapter
import com.xjjk.healthyclients.bean.home.ArticleBean
import com.xjjk.healthyclients.bean.home.CategoryBean
import com.xjjk.healthyclients.databinding.LayoutHealthKnowledgeBinding
import com.xjjk.healthyclients.superfuntion.onCreateTab
/**
* 健康知识 View 组件
* 头部标题 + 搜索栏 + TabLayout(医生说/体检/保健/药品/疾病) + 列表
*/
class HealthKnowledgeView(context: Context?, attrs: AttributeSet?) :
RelativeLayout(context, attrs, 0) {
var mContext: Context? = context
var mBinding: LayoutHealthKnowledgeBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.layout_health_knowledge, this, true
)
private val mAdapter = HealthKnowledgeAdapter()
private var mCategoryList: MutableList<CategoryBean> = mutableListOf()
private var mOnTabSelectedListener: ((CategoryBean) -> Unit)? = null
private var mOnItemClickListener: ((ArticleBean) -> Unit)? = null
private var mOnMoreClickListener: (() -> Unit)? = null
private var mOnSearchClickListener: ((String) -> Unit)? = null
init {
mBinding.rvList.layoutManager = LinearLayoutManager(mContext)
mBinding.rvList.adapter = mAdapter
// 列表项点击
mAdapter.setOnItemClickListener { _, _, position ->
mAdapter.getItem(position)?.let { mOnItemClickListener?.invoke(it) }
}
// 头部更多按钮点击
mBinding.ivMore.setOnClickListener {
mOnMoreClickListener?.invoke()
}
// 健康知识 - 搜索
mBinding.searchView.customSearchTv.setOnClickListener {
val toString = mBinding.searchView.customSearchEt.text.toString()
if (toString.isEmpty()) {
CustomToast.makeText(context, "请输入搜索内容", CustomToast.LENGTH_SHORT).show()
return@setOnClickListener
}
mOnSearchClickListener?.invoke(toString)
}
// 键盘搜索键监听
mBinding.searchView.customSearchEt.setOnEditorActionListener { _, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH
|| (event != null && event.keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN)
) {
val keyword = mBinding.searchView.customSearchEt.text.toString()
if (keyword.isEmpty()) {
CustomToast.makeText(context, "请输入搜索内容", CustomToast.LENGTH_SHORT).show()
return@setOnEditorActionListener true
}
// 隐藏键盘
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.hideSoftInputFromWindow(mBinding.searchView.customSearchEt.windowToken, 0)
mOnSearchClickListener?.invoke(keyword)
true
} else {
false
}
}
}
/**
* 根据栏目列表设置 TabLayout
* @param categories 子栏目列表(健康知识 categoryId=1 的 children
*/
fun setupTabs(categories: MutableList<CategoryBean>) {
mCategoryList.clear()
mCategoryList.addAll(categories)
if (categories.isEmpty()) {
mBinding.tabLayout.visibility = GONE
mBinding.tvEmpty.visibility = VISIBLE
return
}
mBinding.tabLayout.visibility = VISIBLE
mBinding.tvEmpty.visibility = GONE
mBinding.tabLayout.removeAllTabs()
val array = Array<String>(categories.size) {
categories[it].categoryName?:""
}
mBinding.tabLayout.onCreateTab(array,2){index->
mOnTabSelectedListener?.invoke(categories[index!!.position]) ?: return@onCreateTab
}
}
/** 设置文章列表数据(替换) */
fun setArticles(articles: MutableList<ArticleBean>?) {
if (articles.isNullOrEmpty()) {
mBinding.rvList.visibility = GONE
mBinding.tvEmpty.visibility = VISIBLE
return
}
mBinding.rvList.visibility = VISIBLE
mBinding.tvEmpty.visibility = GONE
articles.take(3).let {
mAdapter.setNewInstance(it.toMutableList()) }
}
/** 追加文章列表数据(分页加载) */
fun addArticles(articles: List<ArticleBean>) {
mAdapter.addData(articles)
}
fun setOnTabSelectedListener(listener: (CategoryBean) -> Unit) {
mOnTabSelectedListener = listener
}
fun setOnItemClickListener(listener: (ArticleBean) -> Unit) {
mOnItemClickListener = listener
}
fun setOnMoreClickListener(listener: () -> Unit) {
mOnMoreClickListener = listener
}
fun setOnSearchClickListener(listener: (keyword: String) -> Unit) {
mOnSearchClickListener = listener
}
// 清除搜索框焦点,防止onResume时自动弹出键盘
fun clearSearchFocus() {
mBinding.searchView.customSearchEt.clearFocus()
}
}
@@ -1,6 +1,7 @@
package com.xjjk.healthyclients.view
import android.content.Context
import android.graphics.Color
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
@@ -18,16 +19,19 @@ import com.xjjk.healthyclients.databinding.LayoutHomeItemWatchBinding
import com.xjjk.healthyclients.event.WatchDataChangeEvent
import com.xjjk.healthyclients.fragment.WatchItemFragment
import com.xjjk.healthyclients.superfuntion.init
import com.xjjk.healthyclients.superfuntion.onCreateTab
import org.greenrobot.eventbus.EventBus
class HomeWatchView(context: Context?, attrs: AttributeSet?) :
RelativeLayout(context, attrs, 0), ViewPagerAdapter.OnCreateFragmentListener {
companion object {
/** 自动轮播间隔时间(毫秒) */
private const val AUTO_PLAY_INTERVAL = 5000L
}
private var hasWatch: Boolean = false
// private var tabIndex: Int? = 0
var mContext: Context? = context
var mBinding: LayoutHomeItemWatchBinding = DataBindingUtil.inflate(
@@ -56,20 +60,29 @@ class HomeWatchView(context: Context?, attrs: AttributeSet?) :
fun init(fragment: Fragment) {
val arrayOf = arrayOf("心脑血管", "癌症", "慢呼", "糖尿病", "亚健康")
mBinding.watchTabLayout.onCreateTab(arrayOf, 1) {tab->
if (tab?.position != 0) {
mBinding.emptyText.visibility = View.VISIBLE
mBinding.emptyText.text = "紧急开发中"
mBinding.viewPager.visibility = View.GONE
stopAutoPlay()
} else {
mBinding.viewPager.visibility = View.VISIBLE
mBinding.emptyText.visibility = View.GONE
startAutoPlay()
}
}
// val arrayOf = arrayOf("心脑血管", "癌症", "慢呼", "糖尿病", "亚健康")
// mBinding.watchTabLayout.onCreateTab(arrayOf, 1) { tab ->
// tabIndex =tab?.position
// if (tab?.position != 0) {
// mBinding.emptyText.visibility = View.VISIBLE
// mBinding.emptyText.text = "紧急开发中"
// mBinding.viewPager.visibility = View.GONE
// mBinding.layoutNoWatch.visibility = View.GONE
// stopAutoPlay()
// } else {
// if (!DataStoreManager.isLogin()) {
// showLoginEmpty()
// } else if (hasWatch) {
// mBinding.viewPager.visibility = View.VISIBLE
// mBinding.emptyText.visibility = View.GONE
// mBinding.layoutNoWatch.visibility = View.GONE
// startAutoPlay()
// } else {
// showNoWatchPlaceholder()
// }
// }
//
// }
val tabList: MutableList<TabItemBean> = mutableListOf()
(0..4).forEach { _ ->
@@ -97,19 +110,40 @@ class HomeWatchView(context: Context?, attrs: AttributeSet?) :
}
fun setWatchInfo(watchInfo: WatchInfoBean?) {
// if (tabIndex != 0) {
// return
// }
if (watchInfo?.hasWatch == true) {
hasWatch = true
mBinding.viewPager.visibility = View.VISIBLE
mBinding.watchName.visibility = View.VISIBLE
mBinding.emptyText.visibility = View.GONE
mBinding.layoutNoWatch.visibility = View.GONE
mBinding.watchName.setTextColor(Color.parseColor("#14BEBE"))
mBinding.watchName.text = "监测工具编码:${watchInfo.watchNo}"
} else {
hasWatch = false
mBinding.watchName.setTextColor(Color.parseColor("#808080"))
mBinding.watchName.text = "监测工具编码:暂无信息"
mBinding.viewPager.visibility = View.GONE
mBinding.watchName.visibility = View.GONE
mBinding.emptyText.visibility = View.VISIBLE
mBinding.emptyText.text = "您目前未配备智能穿戴设备"
showNoWatchPlaceholder()
}
}
/** 展示未登录占位文字 */
private fun showLoginEmpty() {
mBinding.viewPager.visibility = View.GONE
mBinding.emptyText.visibility = View.VISIBLE
mBinding.emptyText.text = "暂未登录"
mBinding.layoutNoWatch.visibility = View.GONE
}
/** 展示已登录但未绑定手表的图文占位视图 */
private fun showNoWatchPlaceholder() {
mBinding.viewPager.visibility = View.GONE
mBinding.emptyText.visibility = View.GONE
mBinding.layoutNoWatch.visibility = View.VISIBLE
}
/**
* 接收手表详细数据并通过 EventBus 通知各 WatchItemFragment 刷新
* @param dataList 接口返回的手表各项健康数据列表(索引与 Tab position 对应)
@@ -0,0 +1,78 @@
package com.xjjk.healthyclients.view
import android.app.AlertDialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.TextUtils
import android.view.Gravity
import android.view.LayoutInflater
import android.view.WindowManager
import android.widget.LinearLayout
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import com.sw.healthyclients.data.local.DataStoreManager
import com.sw.healthyclients.view.CustomToast
import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.databinding.DialogBindWatchBinding
import com.xjjk.healthyclients.superfuntion.orEmptyDefault
class WatchBindDialog(context: Context, var listener: (String,Int) -> Unit) : AlertDialog(context) {
lateinit var binding: DialogBindWatchBinding
private var type = 0 // 0 绑定 1 解绑
init {
create()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.dialog_bind_watch, null, false
)
setContentView(binding.root)
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))//设置dialog背景透明
window?.setGravity(Gravity.BOTTOM)
window?.setLayout(
context.resources.displayMetrics.widthPixels,
LinearLayout.LayoutParams.WRAP_CONTENT
);//设置对话框大小
window?.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
setCanceledOnTouchOutside(true)
binding.confirm.setOnClickListener {
if (binding.etWatchid.text.toString().isEmpty()) {
CustomToast.makeText(context, "请输入序列号", Toast.LENGTH_SHORT).show()
} else {
listener.invoke(binding.etWatchid.text.toString(),type)
}
}
binding.btnClose.setOnClickListener {
this.cancel()
}
}
fun showWatchInfo(watchId: String? = null) {
show()
binding.etUserid.setText(DataStoreManager.getUserInfo().workNo)
binding.etUsername.setText(DataStoreManager.getUserInfo().realname)
binding.etWatchid.setText(watchId.orEmptyDefault(""))
if (TextUtils.isEmpty(watchId)) {
binding.etWatchid.isEnabled = true
binding.confirm.setText("绑定")
// type = 0
} else {
binding.etWatchid.isEnabled = false
binding.confirm.isEnabled = false
binding.confirm.alpha = 0.3F
// binding.confirm.setText("解绑")
// type = 1
//
}
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/theme_color" android:state_selected="true" />
<item android:color="@color/text_black_60" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中状态-->
<item android:drawable="@drawable/bg_white_background_shap" android:state_checked="true"/>
<item android:drawable="@drawable/bg_white_background_shap" android:state_selected="true"/>
<!-- 默认状态-->
<item android:drawable="@drawable/bg_action_grey_radius5"/>
</selector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中状态-->
<item android:drawable="@color/theme_color" android:state_checked="true"/>
<item android:drawable="@color/theme_color" android:state_selected="true"/>
<!-- 默认状态-->
<item android:drawable="@color/text_black_60"/>
</selector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中状态-->
<item android:drawable="@drawable/bg_theme_background_shape3" android:state_checked="true"/>
<item android:drawable="@drawable/bg_theme_background_shape3" android:state_selected="true"/>
<!-- 默认状态-->
<item android:drawable="@color/colorWhite"/>
</selector>
@@ -0,0 +1,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1A2EB8B2" />
<corners android:radius="4dp" />
</shape>
@@ -0,0 +1,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1A77859E" />
<corners android:radius="4dp" />
</shape>
@@ -0,0 +1,4 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/emergency_close_bg" />
</shape>
@@ -0,0 +1,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/emergency_primary" />
<corners android:radius="12dp" />
</shape>
@@ -0,0 +1,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="12dp" />
</shape>
@@ -0,0 +1,8 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/emergency_card_selected_bg" />
<stroke
android:width="1dp"
android:color="@color/emergency_card_selected_border" />
<corners android:radius="8dp" />
</shape>
@@ -0,0 +1,8 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/white" />
<stroke
android:width="1dp"
android:color="@color/emergency_card_unselected_border" />
<corners android:radius="8dp" />
</shape>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 渐变下划线装饰:白色 → 主题青色,用于医疗意见章节标题下方 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#FFFFFF"
android:endColor="#2EB8B2"
android:angle="0" />
<size android:height="2dp" />
</shape>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 渐变下划线装饰:白色 → 主题青色,用于医疗意见章节标题下方 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#2EB8B2"
android:endColor="#FFFFFF"
android:angle="0" />
<size android:height="2dp" />
</shape>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 渐变下划线装饰:白色 → 主题青色,用于医疗意见章节标题下方 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#FFFFFF"
android:endColor="#5ABDBA"
android:centerColor="#FFFFFF"
android:centerY="0.5"
android:angle="270" />
<size android:height="2dp" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 灰色圆角背景 #F5F7FB radius 12dp -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F5F7FB" />
<corners android:radius="12dp" />
</shape>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F5F7FB" />
<corners android:radius="8dp" />
</shape>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 患者信息卡片浅绿渐变头部 #DEFFFC → #FFFFFF -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#DEFFFC"
android:endColor="#FFFFFF"
android:angle="270" />
<corners
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />
</shape>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#F24439" />
<size
android:width="6dp"
android:height="6dp" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 表格数据单元格:白色背景 + 浅灰边框 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF" />
<stroke
android:width="1dp"
android:color="#E6E6EA" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 表格表头单元格:灰色背景 + 浅灰边框 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F5F7FB" />
<stroke
android:width="1dp"
android:color="#E6E6EA" />
</shape>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1A2EB8B2" />
<corners android:radius="12dp" />
</shape>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#DEFFFC"
android:endColor="#FFFFFF"
android:angle="270" />
<!--<corners
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />-->
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 青色标签背景 #2EB8B2 radius 12dp,用于"重点关爱"等标签 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2EB8B2" />
<corners android:radius="12dp" />
</shape>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/theme_color" />
<corners android:radius="4dp" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 卡片顶部青色细条,配合患者信息卡片使用 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2EB8B2" />
<corners
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 青色标签背景 #2EB8B2 radius 4dp -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2EB8B2" />
<corners android:radius="4dp" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 小号青色标签背景 #2EB8B2 radius 2dp,用于BMI"标准"标签 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2EB8B2" />
<corners android:radius="2dp" />
</shape>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/theme_color" />
<corners android:radius="3dp" />
</shape>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 卡片顶部青色细条,配合患者信息卡片使用 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2EB8B2" />
<corners
android:topLeftRadius="8dp"
android:topRightRadius="8dp"
android:bottomRightRadius="8dp"
android:bottomLeftRadius="8dp"/>
</shape>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<corners android:radius="12dp" />
</shape>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="@android:color/transparent"
android:pathData="M8,14C11.31,14 14,11.31 14,8C14,4.69 11.31,2 8,2C4.69,2 2,4.69 2,8C2,11.31 4.69,14 8,14Z"
android:strokeWidth="1.2"
android:strokeColor="@color/emergency_primary"
android:strokeLineCap="round" />
<path
android:fillColor="@android:color/transparent"
android:pathData="M8,5L8,8.5L10.5,10"
android:strokeWidth="1.2"
android:strokeColor="@color/emergency_primary"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</vector>
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="14"
android:viewportHeight="14">
<path
android:fillColor="@android:color/transparent"
android:pathData="M1,1L13,13M13,1L1,13"
android:strokeWidth="1.5"
android:strokeColor="@color/emergency_text_gray"
android:strokeLineCap="round" />
</vector>
@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="@android:color/transparent"
android:pathData="M8,14C8,14 14,9.5 14,6C14,2.69 11.31,0 8,0C4.69,0 2,2.69 2,6C2,9.5 8,14 8,14Z"
android:strokeWidth="1"
android:strokeColor="@color/emergency_primary"
android:strokeLineJoin="round" />
<path
android:fillColor="@color/emergency_primary"
android:pathData="M8,8C9.1,8 10,7.1 10,6C10,4.9 9.1,4 8,4C6.9,4 6,4.9 6,6C6,7.1 6.9,8 8,8Z" />
</vector>
@@ -0,0 +1,16 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="@color/emergency_primary" />
</shape>
</item>
<item
android:top="5dp"
android:bottom="5dp"
android:left="5dp"
android:right="5dp">
<shape android:shape="oval">
<solid android:color="@android:color/white" />
</shape>
</item>
</layer-list>
@@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke
android:width="1dp"
android:color="@color/emergency_card_unselected_border" />
<solid android:color="@android:color/white" />
</shape>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -212,6 +212,9 @@
</LinearLayout>
</LinearLayout>
<include
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:id="@+id/lay_question_for_help"
layout="@layout/view_text_question"
app:title="@{@string/archives_detail_question_for_help}"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_50"
android:background="@color/white">
<RelativeLayout
android:id="@+id/custom_ll_search"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_35"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/dp_12"
android:layout_marginRight="10dp"
android:background="@drawable/bg_search_shap_radius23">
<ImageView
android:layout_width="@dimen/dp_15"
android:layout_height="@dimen/dp_15"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/dp_17"
android:src="@mipmap/ic_search" />
<EditText
android:id="@+id/custom_search_et"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/dp_38"
android:background="@null"
android:longClickable="false"
android:gravity="center_vertical"
android:hint="@string/search_hint"
android:maxLines="1"
android:imeOptions="actionSearch"
android:singleLine="true"
android:textColor="@color/text_black_33"
android:textColorHint="#B6B6B6"
android:textSize="@dimen/txt13" />
<com.allen.library.shape.ShapeTextView
android:id="@+id/custom_search_tv"
android:layout_width="55dp"
android:layout_height="28dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/dp_2"
android:gravity="center"
android:text="@string/search_submit"
android:textColor="@color/white"
android:textSize="@dimen/txt14"
app:shapeCornersRadius="14dp"
app:shapeSolidColor="@color/theme_color" />
</RelativeLayout>
</RelativeLayout>
</layout>
@@ -0,0 +1,221 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rectangle_round_top_corner8_white">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/dp_20"
android:text="健康监测工具绑定"
android:textColor="@color/text_black_33"
android:textSize="@dimen/txt16"
android:textStyle="bold" />
<!-- <RelativeLayout-->
<!-- android:id="@+id/userid_layout"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="52dp"-->
<!-- android:layout_below="@+id/tv_title"-->
<!-- android:layout_marginTop="10dp"-->
<!-- android:gravity="center_vertical">-->
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_centerVertical="true"-->
<!-- android:layout_marginLeft="15dp"-->
<!-- android:text="工 号:"-->
<!-- android:textColor="@color/text_black_33"-->
<!-- android:textSize="16sp" />-->
<!-- <com.allen.library.shape.ShapeLinearLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- android:layout_marginLeft="82dp"-->
<!-- android:layout_marginRight="15dp"-->
<!-- app:shapeCornersRadius="8dp"-->
<!-- app:shapeSolidColor="#F5F7FB"-->
<!-- app:shapeStrokeColor="#E6E6EA"-->
<!-- app:shapeStrokeWidth="1dp">-->
<!-- <TextView-->
<!-- android:id="@+id/et_userid"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- android:gravity="start|center_vertical"-->
<!-- android:paddingLeft="8dp"-->
<!-- android:textColor="@color/text_black_33"-->
<!-- android:textSize="16sp"-->
<!-- tools:text="666666" />-->
<!-- </com.allen.library.shape.ShapeLinearLayout>-->
<!-- </RelativeLayout>-->
<!-- <RelativeLayout-->
<!-- android:id="@+id/username_layout"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="52dp"-->
<!-- android:layout_below="@+id/userid_layout"-->
<!-- android:layout_marginTop="10dp"-->
<!-- android:gravity="center_vertical">-->
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_centerVertical="true"-->
<!-- android:layout_marginLeft="15dp"-->
<!-- android:text="姓 名:"-->
<!-- android:textColor="@color/text_black_33"-->
<!-- android:textSize="16sp" />-->
<!-- <com.allen.library.shape.ShapeLinearLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- android:layout_marginLeft="82dp"-->
<!-- android:layout_marginRight="15dp"-->
<!-- app:shapeCornersRadius="8dp"-->
<!-- app:shapeSolidColor="#F5F7FB"-->
<!-- app:shapeStrokeColor="#E6E6EA"-->
<!-- app:shapeStrokeWidth="1dp">-->
<!-- <TextView-->
<!-- android:id="@+id/et_username"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- android:gravity="start|center_vertical"-->
<!-- android:paddingLeft="8dp"-->
<!-- android:textColor="@color/text_black_33"-->
<!-- android:textSize="16sp"-->
<!-- tools:text="666666" />-->
<!-- </com.allen.library.shape.ShapeLinearLayout>-->
<!-- </RelativeLayout>-->
<RelativeLayout
android:id="@+id/username_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_title"
android:layout_marginTop="10dp">
<TextView
android:id="@+id/et_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="15dp"
android:layout_toLeftOf="@+id/et_userid"
android:ellipsize="end"
android:lines="1"
android:textColor="#808080"
android:textSize="15sp"
tools:text="666666" />
<TextView
android:id="@+id/et_userid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="15dp"
android:textColor="#808080"
android:textSize="15sp"
tools:text="666666" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/et_username"
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:text="心脑血管监测工具SN编码:"
android:textColor="@color/text_black_33"
android:textSize="15sp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/watchid_layout"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_below="@+id/username_layout"
android:layout_marginTop="10dp"
android:gravity="center_vertical">
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_centerVertical="true"-->
<!-- android:layout_marginLeft="15dp"-->
<!-- android:text="序列号:"-->
<!-- android:textColor="@color/text_black_33"-->
<!-- android:textSize="16sp" />-->
<com.allen.library.shape.ShapeLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="15dp"
app:shapeCornersRadius="8dp"
app:shapeSolidColor="#FFFFFF"
app:shapeStrokeColor="#E6E6EA"
app:shapeStrokeWidth="1dp">
<EditText
android:id="@+id/et_watchid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:gravity="start|center_vertical"
android:hint="请输入SN编码"
android:paddingLeft="8dp"
android:textColor="@color/text_black_33"
android:textColorHint="#B6B6B6"
android:textSize="16sp" />
</com.allen.library.shape.ShapeLinearLayout>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/watchid_layout"
android:layout_marginTop="20dp"
android:background="#EAECF1" />
<com.allen.library.shape.ShapeButton
android:id="@+id/confirm"
android:layout_width="215dp"
android:layout_height="44dp"
android:layout_below="@id/watchid_layout"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:layout_marginBottom="10dp"
android:gravity="center"
android:text="绑定"
android:textColor="@color/white"
android:textSize="16dp"
android:textStyle="bold"
app:shapeCornersRadius="22dp"
app:shapeSolidColor="#21BEBD" />
<ImageButton
android:id="@+id/btn_close"
android:layout_width="@dimen/dp_40"
android:layout_height="@dimen/dp_40"
android:layout_alignParentRight="true"
android:background="@android:color/transparent"
android:padding="@dimen/dp_15"
android:src="@drawable/ic_close" />
</RelativeLayout>
</layout>
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 全屏半透明背景层,居中显示白色卡片 -->
<FrameLayout
android:id="@+id/fl_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:background="@android:color/transparent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_gravity="center"
android:orientation="vertical"
android:background="@drawable/bg_emergency_dialog_card"
android:paddingBottom="16dp">
<!-- 标题栏 -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="16dp"
android:paddingBottom="12dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="请选择正在应急的工单"
android:textColor="@color/emergency_text_dark"
android:textSize="18sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="end|center_vertical"
android:src="@drawable/ic_emergency_close"
android:background="@drawable/bg_emergency_close_btn"
android:padding="5dp"
android:contentDescription="关闭" />
</FrameLayout>
<!-- 顶部分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/emergency_divider" />
<!-- 工单列表 -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_group_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingTop="12dp"
android:paddingBottom="4dp"
android:maxHeight="420dp"
android:clipToPadding="false"
android:scrollbars="none"
tools:listitem="@layout/item_group_info" />
<!-- 底部分割线 -->
<View
android:id="@+id/view_bottom_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/emergency_divider" />
<!-- 确认选择按钮 -->
<TextView
android:id="@+id/tv_confirm"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="确认选择"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold"
android:background="@drawable/bg_emergency_confirm_btn" />
</LinearLayout>
</FrameLayout>
</layout>
@@ -65,20 +65,22 @@
android:id="@+id/fragment_emergency_call_120"
android:layout_width="@dimen/dp_70"
android:layout_height="@dimen/dp_70"
android:layout_marginRight="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:src="@drawable/ic_emergency_120" />
<ImageView
android:id="@+id/fragment_emergency_seek_doctor"
android:layout_width="@dimen/dp_70"
android:layout_height="@dimen/dp_70"
android:layout_marginLeft="@dimen/dp_16"
android:layout_toRightOf="@+id/fragment_emergency_call_120"
android:src="@drawable/ic_emergency_seek_doctor" />
<ImageView
android:id="@+id/fragment_emergency_paramedic"
android:layout_width="@dimen/dp_70"
android:layout_height="@dimen/dp_70"
android:layout_marginStart="@dimen/dp_16"
android:src="@drawable/ic_paramedic" />
</LinearLayout>
</RelativeLayout>
<androidx.constraintlayout.widget.ConstraintLayout
+54 -7
View File
@@ -26,26 +26,57 @@
android:orientation="vertical"
android:paddingBottom="30dp">
<ImageView
android:id="@+id/head_img"
<com.allen.library.shape.ShapeRelativeLayout
android:layout_width="match_parent"
android:layout_height="450dp"
app:shapeGradientAngle="top_bottom"
app:shapeGradientCenterColor="#3BBCB7"
app:shapeGradientEndColor="#FFFFFF"
app:shapeGradientStartColor="#22C8A0"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="45dp">
<ImageView
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="@drawable/icon_home_head" />
android:background="@mipmap/home_head_img1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="15dp"
android:adjustViewBounds="true"
android:background="@mipmap/home_head_img2" />
</RelativeLayout>
<!-- <ImageView-->
<!-- android:id="@+id/head_img"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:adjustViewBounds="true"-->
<!-- android:scaleType="fitXY"-->
<!-- android:src="@drawable/icon_home_head" />-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="130dp"
android:layout_marginTop="135dp"
android:orientation="vertical">
<com.allen.library.shape.ShapeRelativeLayout
android:visibility="gone"
android:id="@+id/layout_notice"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginHorizontal="12dp"
android:visibility="gone"
app:shapeCornersRadius="8dp"
app:shapeGradientAngle="top_bottom"
app:shapeGradientEndColor="#F2FFFD"
@@ -111,10 +142,11 @@
android:layout_height="60dp"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="10dp" />
<com.xjjk.healthyclients.view.HomeWatchView
android:id="@+id/watch_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
<com.youth.banner.Banner
android:id="@+id/banner3"
@@ -137,7 +169,22 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.xjjk.healthyclients.view.HealthKnowledgeView
android:id="@+id/health_knowledge_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="10dp" />
<com.xjjk.healthyclients.view.HealthConsultationView
android:id="@+id/health_consultation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="10dp" />
<com.youth.banner.Banner
android:layout_marginTop="10dp"
android:id="@+id/banner4"
android:layout_width="match_parent"
android:layout_height="120dp"
+122
View File
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:id="@+id/root_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="16dp"
android:paddingBottom="14dp"
android:background="@drawable/bg_emergency_item_unselected">
<!-- 顶部:姓名 + 选择圆圈 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/tv_user_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/emergency_text_dark"
android:textSize="15sp"
android:textStyle="bold"
android:ellipsize="end"
android:maxLines="1"
tools:text="艾尼瓦尔·吐尔逊" />
<ImageView
android:id="@+id/iv_radio"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginStart="12dp"
android:src="@drawable/ic_emergency_radio_unselected" />
</LinearLayout>
<!-- 部门标签 -->
<TextView
android:id="@+id/tv_dept_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:paddingHorizontal="8dp"
android:paddingVertical="2dp"
android:textColor="@color/emergency_text_gray"
android:textSize="11sp"
android:textStyle="bold"
android:ellipsize="end"
android:maxLines="1"
android:background="@drawable/bg_dept_badge_unselected"
tools:text="员工健康事务部" />
<!-- 内部分割线 -->
<View
android:id="@+id/view_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="10dp"
android:background="@color/emergency_divider" />
<!-- 地址行 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/iv_location"
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_emergency_location" />
<TextView
android:id="@+id/tv_org_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:textColor="@color/emergency_text_body"
android:textSize="13sp"
android:ellipsize="end"
android:maxLines="2"
tools:text="监管中心(石油天然气克拉玛依工程质量监督站)" />
</LinearLayout>
<!-- 时间行 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/iv_clock"
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_emergency_clock" />
<TextView
android:id="@+id/tv_init_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:textColor="@color/emergency_text_gray"
android:textSize="13sp"
android:ellipsize="end"
android:maxLines="1"
tools:text="求助时间:2026-07-22 16:24:32" />
</LinearLayout>
</LinearLayout>
</layout>
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="90dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_15"
android:paddingTop="@dimen/dp_12"
android:paddingRight="@dimen/dp_15">
<!-- 左侧文字区域 -->
<!-- 标题 -->
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="90dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="#111225"
android:textSize="@dimen/txt15"
tools:text="高血压患者日常生活注意事项与饮食指南" />
<!-- 右侧图片 80x60 圆角8 -->
<ImageView
android:id="@+id/iv_cover"
android:layout_width="@dimen/dp_80"
android:layout_height="@dimen/dp_60"
android:layout_alignParentRight="true"
android:layout_marginLeft="@dimen/dp_12"
tools:src="@mipmap/ic_placeholder_rectangle" />
<!-- 收藏和分享信息 -->
<TextView
android:id="@+id/tv_meta"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginTop="@dimen/dp_8"
android:layout_marginBottom="15dp"
android:textColor="@color/text_tab_color"
android:textSize="@dimen/txt12"
tools:text="收藏 128 分享 36" />
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="#EAECF1" />
</RelativeLayout>
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="130dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_15"
android:paddingTop="@dimen/dp_12"
android:paddingRight="@dimen/dp_15">
<!-- 左侧图片 100x100 圆角15 -->
<ImageView
android:id="@+id/iv_cover"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
tools:src="@mipmap/ic_home_item_head_bg" />
<!-- 右侧文字区域 -->
<RelativeLayout
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_120"
android:orientation="vertical">
<!-- 标题 -->
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#252535"
android:textSize="@dimen/txt15"
tools:text="高血压患者日常生活注意事项" />
<!-- 详情 -->
<TextView
android:id="@+id/tv_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_title"
android:layout_marginTop="@dimen/dp_8"
android:ellipsize="end"
android:maxLines="2"
android:textColor="@color/text_tab_color"
android:textSize="@dimen/txt13"
tools:text="高血压是一种常见的慢性疾病,需要长期坚持治疗和管理..." />
</RelativeLayout>
<!-- 底部:标签 + 日期 -->
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="@dimen/dp_120"
android:layout_marginBottom="20dp">
<!-- 标签图标 -->
<ImageView
android:id="@+id/iv_tag_icon"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:src="@mipmap/ic_home_know_tag" />
<!-- 标签文本 -->
<TextView
android:layout_marginRight="60dp"
android:id="@+id/tv_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/dp_4"
android:layout_toRightOf="@+id/iv_tag_icon"
android:textColor="#14BEBE"
android:textSize="@dimen/txt11"
android:lines="1"
android:ellipsize="end"
tools:text="心血管心血管心血管心血管心血管心血管心血管心血管心血管心血管" />
<!-- 日期 -->
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textColor="@color/text_tab_color"
android:textSize="@dimen/txt11"
tools:text="2024-06-29" />
</RelativeLayout>
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="120dp"
android:background="#EAECF1" />
</RelativeLayout>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_tab_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center"
android:includeFontPadding="false"
android:paddingHorizontal="6dp"
android:paddingVertical="7dp"
android:textColor="@color/color_child_tab_layout_text_index_theme"
android:textSize="14sp" />
<View
android:id="@+id/tv_line"
android:layout_width="18dp"
android:layout_height="3dp"
android:layout_below="@+id/tv_text"
android:layout_centerHorizontal="true"
android:background="@drawable/bg_child_tab_layout_item_line" />
</RelativeLayout>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/dp_10"
android:background="@drawable/bg_white_background_shap_radius12"
>
<!-- 头部:标题 + 更多箭头 -->
<RelativeLayout
android:id="@+id/rl_header"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@mipmap/ic_home_item_head_bg"
android:scaleType="fitXY">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="8dp"
android:ellipsize="end"
android:text="健康资讯"
android:textColor="@color/text_black_33"
android:textSize="16sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_more"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignParentRight="true"
android:layout_marginTop="12dp"
android:layout_marginRight="15dp"
android:background="@drawable/arrow_right"
android:padding="10dp" />
</RelativeLayout>
<!-- 列表 -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:paddingTop="@dimen/dp_10" />
<!-- 空状态提示 -->
<TextView
android:id="@+id/tv_empty"
android:layout_width="match_parent"
android:layout_height="80dp"
android:gravity="center"
android:text="暂无健康咨询"
android:textColor="#14BEBE"
android:textSize="16sp"
android:visibility="gone" />
</LinearLayout>
</layout>
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data></data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/dp_10"
android:background="@drawable/bg_white_background_shap_radius12"
android:orientation="vertical">
<!-- 头部:标题 + 更多箭头 -->
<RelativeLayout
android:id="@+id/rl_header"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@mipmap/ic_home_item_head_bg"
android:scaleType="fitXY">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="8dp"
android:ellipsize="end"
android:text="健康知识"
android:textColor="@color/text_black_33"
android:textSize="16sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_more"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignParentRight="true"
android:layout_marginTop="12dp"
android:layout_marginRight="15dp"
android:background="@drawable/arrow_right"
android:padding="10dp" />
</RelativeLayout>
<!-- 搜索栏 -->
<include
android:id="@+id/search_view"
layout="@layout/custom_search_view_home" />
<!-- Tab 标签栏 -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:background="@color/white"
app:tabIndicatorFullWidth="false"
app:tabIndicatorHeight="@dimen/dp_0"
app:tabMinWidth="@dimen/dp_0"
app:tabMode="fixed"
app:tabPaddingEnd="@dimen/dp_0"
app:tabPaddingStart="@dimen/dp_0"
app:tabRippleColor="@null" />
<!-- 分隔线 -->
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:background="@color/line" />
<!-- 列表 -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:paddingTop="@dimen/dp_10" />
<!-- 空状态提示 -->
<TextView
android:id="@+id/tv_empty"
android:layout_width="match_parent"
android:layout_height="80dp"
android:gravity="center"
android:text="暂无健康知识"
android:textColor="#14BEBE"
android:textSize="16sp"
android:visibility="gone" />
</LinearLayout>
</layout>
@@ -6,9 +6,9 @@
<RelativeLayout
android:layout_marginHorizontal="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="15dp">
<RelativeLayout
@@ -43,6 +43,7 @@
tools:text="监测工具编码:4PMBB25A28100553" />
<ImageView
android:id="@+id/img_watch_more"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignParentRight="true"
@@ -62,20 +63,20 @@
app:shapeCornersBottomLeftRadius="12dp"
app:shapeSolidColor="@color/white">
<com.google.android.material.tabs.TabLayout
android:id="@+id/watch_tab_layout"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_below="@+id/toolbar_lay"
android:layout_marginTop="5dp"
android:background="@color/white"
app:tabIndicatorFullWidth="false"
app:tabIndicatorHeight="@dimen/dp_0"
app:tabMinWidth="@dimen/dp_0"
app:tabMode="auto"
app:tabPaddingEnd="@dimen/dp_0"
app:tabPaddingStart="@dimen/dp_0"
app:tabRippleColor="@null" />
<!-- <com.google.android.material.tabs.TabLayout-->
<!-- android:id="@+id/watch_tab_layout"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="40dp"-->
<!-- android:layout_below="@+id/toolbar_lay"-->
<!-- android:layout_marginTop="5dp"-->
<!-- android:background="@color/white"-->
<!-- app:tabIndicatorFullWidth="false"-->
<!-- app:tabIndicatorHeight="@dimen/dp_0"-->
<!-- app:tabMinWidth="@dimen/dp_0"-->
<!-- app:tabMode="auto"-->
<!-- app:tabPaddingEnd="@dimen/dp_0"-->
<!-- app:tabPaddingStart="@dimen/dp_0"-->
<!-- app:tabRippleColor="@null" />-->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
@@ -91,15 +92,43 @@
<TextView
android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="163dp"
android:layout_height="64dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="55dp"
android:layout_marginTop="5dp"
android:background="@color/white"
android:gravity="center"
android:text="暂未登录"
android:textColor="#77849E"
android:textSize="15sp" />
<!-- 已登录但未绑定手表的占位视图 -->
<LinearLayout
android:id="@+id/layout_no_watch"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_below="@+id/watch_tab_layout"
android:gravity="center"
android:orientation="vertical">
<com.allen.library.shape.ShapeTextView
android:layout_gravity="center"
android:id="@+id/btn_bind_watch"
android:layout_width="80dp"
android:layout_height="24dp"
android:layout_marginTop="5dp"
android:gravity="center"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="去绑定"
android:textColor="@color/theme_color"
android:textSize="13sp"
app:shapeCornersRadius="16dp"
app:shapeSolidColor="@color/colorWhite"
app:shapeStrokeColor="@color/theme_color"
app:shapeStrokeWidth="1dp" />
</LinearLayout>
</com.allen.library.shape.ShapeRelativeLayout>
</RelativeLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Some files were not shown because too many files have changed in this diff Show More