feat(watch): 新增手表绑定/解绑功能

- 新增 WatchBindDialog 对话框,支持手表序列号输入和绑定/解绑操作
- HomeApi 新增 getBindDevice 和 getUnbundleDevice 接口
- HomeRepository 新增对应的绑定/解绑方法
- HomeViewModel 新增 watchBindStatus 状态流,用于监听绑定结果
- HomeFragment 新增手表绑定对话框交互逻辑
- 优化手表头部点击事件,已绑定时跳转详情,未绑定时显示绑定对话框
- 完善手表信息展示和状态管理
This commit is contained in:
zhanglei
2026-03-16 09:29:21 +08:00
parent d0a92cea60
commit 06352cbf11
6 changed files with 282 additions and 139 deletions
@@ -1,10 +1,10 @@
package com.xjjk.healthyclients.data.api
import com.xjjk.healthyclients.bean.home.EncryptInfoBean
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
@@ -92,5 +92,16 @@ 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>
}
@@ -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)
}
}
}
@@ -16,6 +16,7 @@ import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.adapter.common.MultiItemTypeAdapter
import com.xjjk.healthyclients.base.BaseVMBFragment
import com.xjjk.healthyclients.bean.guidance.GuidanceListBean
import com.xjjk.healthyclients.bean.home.WatchInfoBean
import com.xjjk.healthyclients.databinding.FragmentHomeBinding
import com.xjjk.healthyclients.event.HomeTabChangeEvent
import com.xjjk.healthyclients.retrofit.UrlConfig
@@ -35,6 +36,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 +50,15 @@ class HomeFragment :
BaseVMBFragment<HomeViewModel, FragmentHomeBinding>(R.layout.fragment_home),
SwipeRefreshLayout.OnRefreshListener {
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
/** 通知轮播协程 Job,重新启动前先取消旧任务,防止多协程并发叠加 */
private var noticeJob: Job? = null
@@ -62,6 +67,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
@@ -136,13 +149,35 @@ 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?) {
@@ -323,6 +358,7 @@ class HomeFragment :
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
mViewModel.watchInfo.collectLatest { bean ->
watchInfo = bean
mBinding.watchView.setWatchInfo(bean)
if (bean?.hasWatch == true) {//有手表的情况下去获取手表数据
mViewModel.getHomeWatchData(DateUtil.nowStringDateShort)
@@ -338,6 +374,15 @@ class HomeFragment :
}
}
}
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 ->
@@ -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)
})
}
)
}
}
@@ -4,39 +4,33 @@ 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.text.TextUtils
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.LinearLayout
import android.widget.RatingBar
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.DialogAppraiseBinding
import com.xjjk.healthyclients.databinding.DialogBindWatchBinding
import com.xjjk.healthyclients.superfuntion.orEmptyDefault
class AppraiseDialog constructor(
context: Context
) : AlertDialog(context) {
lateinit var mOnSubmitClickListener: OnSubmitClickListener
lateinit var binding: DialogAppraiseBinding
class WatchBindDialog(context: Context, var listener: (String,Int) -> Unit) : AlertDialog(context) {
lateinit var binding: DialogBindWatchBinding
private var type = 0 // 0 绑定 1 解绑
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(
LayoutInflater.from(context),
R.layout.dialog_appraise, null, false
R.layout.dialog_bind_watch, null, false
)
setContentView(binding.root)
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))//设置dialog背景透明
@@ -46,73 +40,37 @@ class AppraiseDialog constructor(
LinearLayout.LayoutParams.WRAP_CONTENT
);//设置对话框大小
window?.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
binding.btnSubmit.setOnClickListener {
if (getRating()==0F) {
CustomToast.makeText(context, "您还没有选择星级评分", Toast.LENGTH_SHORT).show()
return@setOnClickListener
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)
}
if (this::mOnSubmitClickListener.isInitialized) {
mOnSubmitClickListener?.onSubmitClick(this@AppraiseDialog)
}
this.cancel()
}
binding.btnClose.setOnClickListener {
if (this::mOnSubmitClickListener.isInitialized) {
mOnSubmitClickListener?.onCancelClick(this@AppraiseDialog)
}
this.cancel()
}
}
fun setOnSubmitClickListener(onSubmitClickListener: OnSubmitClickListener): AppraiseDialog {
this.mOnSubmitClickListener = onSubmitClickListener
return this
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.setText("解绑")
type = 1
}
interface OnSubmitClickListener {
fun onSubmitClick(viewDialog: AppraiseDialog)
fun onCancelClick(viewDialog: AppraiseDialog){}
}
fun setDialogTitle(title: String, titleSize: Float = 18f): AppraiseDialog {
binding.tvTitle.visibility = View.VISIBLE
binding.tvTitle.text = title
binding.tvTitle.textSize = titleSize
return this
}
fun setContentHint(text: String, textSize: Float = 13f): AppraiseDialog {
binding.etAppraise.hint = text
binding.etAppraise.textSize = textSize
return this
}
fun setBtnText(text: String, btnTextSize: Float = 18f): AppraiseDialog {
binding.btnSubmit.text = text
binding.btnSubmit.textSize = btnTextSize
return this
}
fun setDialogCancelable(flag: Boolean): AppraiseDialog {
setCancelable(flag)
return this
}
fun getAppraiseContext(): String {
return binding.etAppraise.text.trim().toString()
}
fun getRatingBar(): RatingBar {
return binding.ratingBar
}
fun getRating(): Float {
return getRatingBar().rating
}
/**
* 获取匿名状态
*/
fun getAnonymityStatus(): Boolean {
return binding.cbAnonymity.isChecked
}
}
+145 -54
View File
@@ -10,73 +10,164 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rectangle_top_round_corner20_white">
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/txt14"
android:text="@string/dialog_appraise_title"/>
<RatingBar
android:id="@+id/rating_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"
style="@style/Widget.AppCompat.RatingBar.Indicator"
android:layout_marginTop="@dimen/dp_24"
android:layout_marginHorizontal="@dimen/dp_12"
android:layout_centerHorizontal="true"
android:numStars="5"
android:stepSize="0.5"
android:rating="0"
android:isIndicator="false"
android:progressTint="@color/rating_bar_color"
android:secondaryProgressTint="@color/rating_bar_color"/>
<EditText
android:id="@+id/et_appraise"
android:longClickable="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@+id/rating_bar"
android:layout_marginTop="@dimen/dp_18"
android:layout_marginHorizontal="@dimen/dp_15"
android:background="@drawable/rectangle_round_corner10_gray"
android:paddingHorizontal="@dimen/dp_12"
android:paddingVertical="@dimen/dp_9"
android:minHeight="@dimen/dp_130"
android:gravity="top"
android:textColor="@color/text_black_33"
android:textSize="@dimen/txt13"
android:hint="@string/dialog_appraise_hint"/>
<CheckBox
android:id="@+id/cb_anonymity"
android:layout_marginTop="10dp"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:layout_alignLeft="@+id/et_appraise"
android:layout_below="@+id/et_appraise"
android:button="@drawable/check_theme_style"
android:paddingLeft="@dimen/dp_8"
android:checked="false"
android:text="@string/dialog_appraise_anonymity"/>
<Button
android:id="@+id/btn_submit"
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:background="@drawable/rectangle_round_corner20_theme"
android:text="@string/dialog_appraise_submit"
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/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_marginLeft="82dp"
android:layout_marginRight="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:paddingLeft="8dp"
android:textColor="@color/text_black_33"
android:textSize="16sp"
tools:text="666666" />
</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:minHeight="@dimen/dp_0"
android:layout_below="@+id/cb_anonymity"
android:layout_marginHorizontal="@dimen/dp_48"
android:layout_marginVertical="@dimen/dp_24"
android:outlineProvider="none"
android:textSize="@dimen/txt14"
android:paddingVertical="@dimen/dp_11"/>
android:textSize="16dp"
android:textStyle="bold"
app:shapeCornersRadius="22dp"
app:shapeSolidColor="#21BEBD" />
<ImageButton
android:id="@+id/btn_close"
android:layout_width="@dimen/dp_40"