调整了部分问题

This commit is contained in:
zxj
2025-07-23 14:22:11 +08:00
parent 5ad82eb224
commit 7fd2ccb960
28 changed files with 486 additions and 210 deletions
@@ -6,6 +6,11 @@ object GlobalData {
*/
var globalEquipmentCode: String = "202501171634"
/**
* app版本号
*/
var appVersion: String = ""
/**
* 横排数量
*/
@@ -17,9 +22,11 @@ object GlobalData {
var arrayVertical: Int = 11
/**
* 排列方式
* 排列方式 0 垂直 1 水平
*/
var arrayMode: Int = 0
var arrayMode: Int = 1
}
/**
@@ -1,6 +1,7 @@
package com.sw.platecabinet
import com.sw.plate.App
import com.sw.plate.utils.AppUtil
import timber.log.Timber
class MyApp : App() {
@@ -12,5 +13,14 @@ class MyApp : App() {
super.onCreate()
Timber.plant(Timber.DebugTree())
Timber.d("初始化")
initGlobalData()
}
/**
* 初始化全局数据
*/
private fun initGlobalData() {
GlobalData.appVersion = AppUtil.getAppVersionName(this)
GlobalData.globalEquipmentCode = "202501171634"
}
}
@@ -5,24 +5,34 @@ import android.content.Context
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.view.KeyEvent
import android.view.View
import android.view.WindowInsetsController
import android.view.WindowManager
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.sw.inbound.utils.DateTimeUtils
import com.sw.plate.utils.ScanGunKeyEventHelper
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.comn.SerialApi
import com.sw.plate.utils.comn.SerialPortManager
import com.sw.platecabinet.R
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.ext.setClickListeners
import com.sw.platecabinet.utils.PermissionHelper
import com.sw.platecabinet.view.CustomDialog
import com.sw.platecabinet.viewmodel.SettingViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
/**
* activity 基类
@@ -34,6 +44,8 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
private var timeJob: Job? = null
private var mDialogWaiting: CustomDialog? = null
private val permissionHelpers = mutableMapOf<Int, PermissionHelper>()
protected var keyEventHelper: ScanGunKeyEventHelper? = null
private val viewModel by viewModels<SettingViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -49,6 +61,38 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
updateTime()
registerDataChange()
initialize()
registerKeyEvent()
}
/**
* 监听扫描枪扫描事件
*/
protected fun registerKeyEvent() {
keyEventHelper =
ScanGunKeyEventHelper(context, object : ScanGunKeyEventHelper.OnScanSuccessListener {
override fun onScanSuccess(barcode: String?) {
Timber.d("onScanSuccess barcode = $barcode")
if (barcode == null) return
handleScanKeyInfo(barcode)
}
})
}
/**
* 处理扫描枪数据
*/
protected open fun handleScanKeyInfo(scanInfo: String) {
viewModel.findByPlateNumber(plateNumber = scanInfo)
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (keyEventHelper != null) {
if (keyEventHelper!!.isScanGunEvent(event)) {
keyEventHelper!!.analysisKeyEvent(event)
return true
}
}
return super.dispatchKeyEvent(event)
}
fun updateTime() {
@@ -74,10 +118,16 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
}
/**
* 左侧日期双击
*/
open fun onLeftDoubleClick() {
}
/**
* 右侧时间双击
*/
open fun onRightDoubleClick() {
MainActivity.start(context, pageType = PageType.SETTING_LIST)
}
@@ -139,6 +189,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
}
private fun enforceImmersiveMode() {
// 持续强制隐藏系统栏(防止手势触发)
window.decorView.postDelayed({
@@ -177,10 +228,62 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected abstract fun initialize()
protected open fun registerDataChange() {}
/**
* 注册数据监听
*/
protected open fun registerDataChange() {
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
lifecycleScope.launch {
viewModel.currentUserInfo.drop(1).collect {
if (it == null) return@collect
if (it.equipmentBoxCode?.isNotEmpty() == true) {
SerialApi.openPlate(
it.equipmentBoxCode.toInt(),
object : SerialPortManager.SendCallback {
override fun onSuccess() {
MainActivity.start(context, pageType = PageType.PLATE_OPEN)
}
override fun onFail(e: Exception?) {
ToastUtils.showToast("柜门打开失败")
}
})
} else {
viewModel.getEquipmentList()
}
}
}
lifecycleScope.launch {
viewModel.equipmentList.drop(1).collect {
if (it.isEmpty()) return@collect
val unbindList = it.filter { !it.isBound() }
if (unbindList.isEmpty()) {
MainActivity.start(context = context, pageType = PageType.PLATE_CABINET_FULL)
} else {
val firstInfo = unbindList[0]
val currentUserInfo = viewModel.currentUserInfo.value
firstInfo.plateNumber = currentUserInfo?.plateNumber ?: ""
MainActivity.start(
context = context,
pageType = PageType.BIND_PLATE,
equipmentUserInfo = firstInfo
)
}
}
}
}
override fun onDestroy() {
timeJob?.cancel()
keyEventHelper?.onDestroy()
super.onDestroy()
}
}
@@ -32,6 +32,7 @@ import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.utils.PermissionHelper
import com.sw.platecabinet.viewmodel.UserViewModel
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -73,8 +74,9 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.currentUserInfo.collect {
viewModel.currentUserInfo.drop(1).collect {
Timber.d("currentUserInfo it = $it")
if (it != null) {
ToastUtils.showToast("登录成功")
@@ -83,15 +85,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}
}
}
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
}
private fun checkCameraPermission() {
@@ -314,7 +307,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
)
)
.rotation(windowManager.defaultDisplay.rotation)
.additionalRotation(90) // 角度
.additionalRotation(0) // 角度
.previewSize(recognizeViewModel.loadPreviewSize())
.specificCameraId(previewConfig.rgbCameraId)
.isMirror(true)
@@ -7,6 +7,7 @@ import com.sw.plate.utils.ToastUtils
import com.sw.platecabinet.databinding.ActivityLoginByPwdBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.viewmodel.UserViewModel
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -34,28 +35,23 @@ class LoginByPwdActivity : BaseActivity<ActivityLoginByPwdBinding>() {
binding.tvFaceRec.setOnClickListener {
val intent = Intent(this, LoginByFaceActivity::class.java)
startActivity(intent)
// handleScanKeyInfo(binding.etPwd.text.toString())
}
}
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.currentUserInfo.collect {
Timber.d("currentUserInfo it = $it")
if (it != null) {
ToastUtils.showToast("登录成功")
MainActivity.start(context, pageType = PageType.PLATE_CABINET_FULL)
finish()
viewModel.currentUserInfo
.drop(1)
.collect {
Timber.d("currentUserInfo it = $it")
if (it != null) {
// ToastUtils.showToast("登录成功")
MainActivity.start(context, pageType = PageType.PLATE_OPEN)
finish()
}
}
}
}
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
}
@@ -13,9 +13,12 @@ import com.sw.platecabinet.fragment.UnBindPlateFragment
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.utils.FragmentHelper
typealias Callback = (String) -> Unit
class MainActivity : BaseActivity<ActivityMainBinding>() {
private lateinit var fragmentHelper: FragmentHelper
private var pageType = PageType.SETTING_LIST
private var callback: Callback? = null
override fun inflateViewBinding(): ActivityMainBinding {
return ActivityMainBinding.inflate(layoutInflater)
@@ -65,8 +68,23 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun onRightDoubleClick() {
}
override fun handleScanKeyInfo(scanInfo: String) {
if (callback != null) {
callback!!.invoke(scanInfo)
}
}
fun registerKeyEventInfo(callback: Callback) {
this.callback = callback
}
override fun registerDataChange() {
super.registerDataChange()
}
}
/**
* 界面类型
*/
@@ -25,7 +25,18 @@ class GenericPageAdapter<T, VB : ViewBinding>(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PageViewHolder {
val view = LayoutInflater.from(parent.context).inflate(pageLayoutId, parent, false)
return PageViewHolder(view)
val holder = PageViewHolder(view)
// 在创建ViewHolder时设置ItemDecoration,只设置一次
holder.childRecyclerView.addItemDecoration(
GridSpacingItemDecoration(
spanCount = 2,
spacing = view.context.dpToPx(12),
includeEdge = true // 包含边缘间距
)
)
return holder
}
override fun onBindViewHolder(holder: PageViewHolder, position: Int) {
@@ -35,19 +46,13 @@ class GenericPageAdapter<T, VB : ViewBinding>(
override fun getItemCount(): Int = pages.size
inner class PageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val childRecyclerView: RecyclerView = itemView.findViewById(R.id.childRecyclerView)
val childRecyclerView: RecyclerView = itemView.findViewById(R.id.childRecyclerView)
init {
childRecyclerView.layoutManager = GridLayoutManager(itemView.context, 2)
}
fun bind(items: List<T>) {
childRecyclerView.layoutManager = GridLayoutManager(itemView.context, 2)
childRecyclerView.addItemDecoration(
GridSpacingItemDecoration(
spanCount = 2, // 2列
spacing = itemView.context.dpToPx(6), // 12dp
includeEdge = true // 包含边缘间距
)
)
childRecyclerView.adapter =
GenericItemAdapter(items, itemBindingInflater, itemBindCallback)
}
@@ -8,9 +8,13 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.sw.platecabinet.R
import com.sw.platecabinet.view.CustomDialog
import com.sw.platecabinet.viewmodel.SettingViewModel
import kotlinx.coroutines.launch
abstract class BaseFragment<VB : ViewBinding>(
private val bindingInflater: (inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> VB
@@ -18,6 +22,7 @@ abstract class BaseFragment<VB : ViewBinding>(
private var _binding: VB? = null
protected val binding get() = _binding!!
private var mDialogWaiting: CustomDialog? = null
protected val viewModel by viewModels<SettingViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
@@ -33,7 +38,17 @@ abstract class BaseFragment<VB : ViewBinding>(
/**
* 注册数据变化监听
*/
open fun registerDataChange() {}
open fun registerDataChange() {
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
}
abstract fun initialize()
@@ -4,14 +4,15 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.sw.plate.utils.ToastUtils
import com.sw.platecabinet.R
import com.sw.platecabinet.activity.MainActivity
import com.sw.platecabinet.adapter.GenericItemAdapter
import com.sw.platecabinet.adapter.GridSpacingItemDecoration
import com.sw.platecabinet.adapter.dpToPx
@@ -21,7 +22,7 @@ import com.sw.platecabinet.ext.maskName
import com.sw.platecabinet.ext.maskPhone
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.model.response.SearchResult
import com.sw.platecabinet.viewmodel.SettingViewModel
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -32,7 +33,6 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
FragmentBindPlateBinding::inflate
) {
private lateinit var adapter: GenericItemAdapter<SearchResult.Member, ItemSearchUserInfoBinding>
private val viewModel by viewModels<SettingViewModel>()
internal val ARG_PARAM1 = "param1"
private var info: EquipmentUserInfo? = null
private var checkedItem: SearchResult.Member? = null
@@ -53,6 +53,9 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
info = it.getParcelable(ARG_PARAM1)
info?.let {
binding.tvNum.text = "${it.equipmentName}-${it.equipmentBoxCode}"
if (!TextUtils.isEmpty(it.plateNumber)) {
binding.etCode.text = Editable.Factory.getInstance().newEditable(it.plateNumber)
}
}
}
initView()
@@ -88,6 +91,12 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
}
}
})
(activity as MainActivity).registerKeyEventInfo {
binding.etCode.text = Editable.Factory.getInstance().newEditable(it)
}
}
private fun initListener() {
binding.etUserInfo.addTextChangedListener(object : TextWatcher {
private val handler = Handler(Looper.getMainLooper())
private val debounceDelay = 500L // 延迟 500 毫秒
@@ -120,9 +129,6 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
}, debounceDelay)
}
})
}
private fun initListener() {
binding.llBind.setOnClickListener {
if (info == null || info!!.equipmentCode?.isEmpty() == true || info!!.equipmentBoxCode?.isEmpty() == true) {
ToastUtils.showToast("传入的设备信息异常")
@@ -156,13 +162,14 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
* 监听数据变化
*/
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.searchMemberList.collect {
viewModel.searchMemberList.drop(1).collect {
adapter.updateData(it)
}
}
lifecycleScope.launch {
viewModel.bindStateChange.collect {
viewModel.bindStateChange.drop(1).collect {
if (it.first == null) return@collect // 解绑状态过滤
val errorInfo = it.second
if (errorInfo.isSuccess()) {
@@ -173,15 +180,6 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
}
}
}
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
}
private fun createAdapter(): GenericItemAdapter<SearchResult.Member, ItemSearchUserInfoBinding> {
@@ -1,24 +1,28 @@
package com.sw.platecabinet.fragment
import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.sw.inbound.utils.DateTimeUtils
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.comn.SerialApi
import com.sw.plate.utils.comn.SerialPortManager
import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.R
import com.sw.platecabinet.activity.MainActivity
import com.sw.platecabinet.activity.PageType
import com.sw.platecabinet.adapter.GenericItemAdapter
import com.sw.platecabinet.adapter.GenericPageAdapter
import com.sw.platecabinet.adapter.GridSpacingItemDecoration
import com.sw.platecabinet.adapter.dpToPx
import com.sw.platecabinet.databinding.FragmentSettingListBinding
import com.sw.platecabinet.databinding.ItemBindViewBinding
import com.sw.platecabinet.ext.formatNumber
import com.sw.platecabinet.ext.maskName
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.viewmodel.SettingViewModel
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.math.ceil
@@ -28,35 +32,47 @@ import kotlin.math.ceil
*/
class SettingListFragment :
BaseFragment<FragmentSettingListBinding>(FragmentSettingListBinding::inflate) {
private val viewModel: SettingViewModel by viewModels<SettingViewModel>()
private lateinit var pageAdapter: GenericPageAdapter<EquipmentUserInfo, ItemBindViewBinding>
private lateinit var itemAdapter: GenericItemAdapter<EquipmentUserInfo, ItemBindViewBinding>
private val itemsPerPage = 22 // 每行2个,每列11个,共22个
/**
* 需要横向滚动的adapter
*/
private var pageAdapter: GenericPageAdapter<EquipmentUserInfo, ItemBindViewBinding>? = null
/**
* 垂直滚动的adapter
*/
private var itemAdapter: GenericItemAdapter<EquipmentUserInfo, ItemBindViewBinding>? = null
private val itemsPerPage = GlobalData.arrayCross * GlobalData.arrayVertical // 每行2个,每列11个,共22个
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.equipmentList.collect { it ->
viewModel.equipmentList.drop(1).collect { it ->
Timber.d("registerDateChange updateAdapter it = $it")
updateAdapter(it)
}
}
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
}
override fun initialize() {
binding.mainRecyclerView.apply {
layoutManager = LinearLayoutManager(context)
pageAdapter = createAdapter()
adapter = pageAdapter
if (GlobalData.arrayCross > 2) {
layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
pageAdapter = createPageAdapter()
adapter = pageAdapter
} else {
layoutManager = GridLayoutManager(context, 2)
// 添加间距装饰(12dp)
addItemDecoration(
GridSpacingItemDecoration(
spanCount = 2,
spacing = requireContext().dpToPx(12),
includeEdge = true // 包含边缘间距
)
)
itemAdapter = createItemAdapter()
adapter = itemAdapter
}
}
}
@@ -66,94 +82,115 @@ class SettingListFragment :
viewModel.getEquipmentList()
}
private fun createAdapter(): GenericPageAdapter<EquipmentUserInfo, ItemBindViewBinding> {
private fun createItemAdapter(): GenericItemAdapter<EquipmentUserInfo, ItemBindViewBinding> {
return GenericItemAdapter(
items = emptyList(),
bindingInflater = ItemBindViewBinding::inflate,
bindCallback = { item, position ->
setItemInfo(item, position)
}
)
}
private fun createPageAdapter(): GenericPageAdapter<EquipmentUserInfo, ItemBindViewBinding> {
return GenericPageAdapter(
pages = emptyList(),
pageLayoutId = R.layout.page_item_layout,
itemBindingInflater = ItemBindViewBinding::inflate,
itemBindCallback = { item, position ->
this.tvNum.text = formatNumber(item.equipmentBoxCode ?: "")
if (item.isBound()) {
val date = DateTimeUtils.parseDateTime(item.updateTime)
val timeInMillis = date?.time ?: 0L
val isOldTime = DateTimeUtils.isMoreThan36HoursFromNow(timeInMillis)
this.llRoot.setBackgroundResource(R.drawable.grid_item_bind)
this.tvNum.setTextColor(resources.getColor(R.color.bind_4E535D))
this.tvLastTime.setTextColor(
if (isOldTime) resources.getColor(R.color.bind_time_old) else resources.getColor(
R.color.bind_585868
)
)
this.llOpen.setBackgroundResource(R.drawable.grid_button_bind)
this.tvOpen.setTextColor(resources.getColor(R.color.bind_F0C8B4))
this.llBindInfo.visibility = View.VISIBLE
this.tvUnbind.visibility = View.GONE
this.tvName.text = item.name.maskName()
this.tvLastTime.text = DateTimeUtils.getTimeAgo(date)
} else {
this.llRoot.setBackgroundResource(R.drawable.grid_item_unbind)
this.tvNum.setTextColor(resources.getColor(R.color.bind_F0C8B4))
this.llOpen.setBackgroundResource(R.drawable.grid_button_unbind)
this.tvOpen.setTextColor(resources.getColor(R.color.unbind_32283C))
this.llBindInfo.visibility = View.GONE
this.tvUnbind.visibility = View.VISIBLE
}
this.llRoot.setOnClickListener {
Timber.d("itemClick llRoot ${item.name}, position = $position")
if (item.isBound()) {
MainActivity.start(
requireContext(),
pageType = PageType.UNBIND_PLATE,
equipmentUserInfo = item
)
} else {
MainActivity.start(
requireContext(),
pageType = PageType.BIND_PLATE,
equipmentUserInfo = item
)
}
}
this.llOpen.setOnClickListener {
Timber.d("itemClick llOpen equipmentBoxCode = ${item.equipmentBoxCode}")
val equipmentBoxCode: Int = item.equipmentBoxCode?.toInt() ?: -1
if (equipmentBoxCode < 0) {
ToastUtils.showToast("餐盘柜编号错误")
return@setOnClickListener
}
SerialApi.openPlate(equipmentBoxCode, object : SerialPortManager.SendCallback {
override fun onSuccess() {
MainActivity.start(requireContext(), pageType = PageType.PLATE_OPEN)
}
override fun onFail(e: Exception?) {
ToastUtils.showToast("柜门打开失败")
}
})
}
setItemInfo(item, position)
}
)
}
fun updateAdapter(items: List<EquipmentUserInfo>) {
// if (GlobalData.arrayCross > 2) {
var pages = if (items.size > itemsPerPage) {
items.chunked(itemsPerPage) {
convertToColumnFirst(it, 11)
}
} else {
listOf(
// 根据条件判断是否需要重新排列
// items
convertToColumnFirst(items, 11)
private fun ItemBindViewBinding.setItemInfo(
item: EquipmentUserInfo,
position: Int
) {
this.tvNum.text = formatNumber(item.equipmentBoxCode ?: "")
if (item.isBound()) {
val date = DateTimeUtils.parseDateTime(item.updateTime)
val timeInMillis = date?.time ?: 0L
val isOldTime = DateTimeUtils.isMoreThan36HoursFromNow(timeInMillis)
this.llRoot.setBackgroundResource(R.drawable.grid_item_bind)
this.tvNum.setTextColor(resources.getColor(R.color.bind_4E535D))
this.tvLastTime.setTextColor(
if (isOldTime) resources.getColor(R.color.bind_time_old) else resources.getColor(
R.color.bind_585868
)
)
this.llOpen.setBackgroundResource(R.drawable.grid_button_bind)
this.tvOpen.setTextColor(resources.getColor(R.color.bind_F0C8B4))
this.llBindInfo.visibility = View.VISIBLE
this.tvUnbind.visibility = View.GONE
this.tvName.text = item.name.maskName()
this.tvLastTime.text = DateTimeUtils.getTimeAgo(date)
} else {
this.llRoot.setBackgroundResource(R.drawable.grid_item_unbind)
this.tvNum.setTextColor(resources.getColor(R.color.bind_F0C8B4))
this.llOpen.setBackgroundResource(R.drawable.grid_button_unbind)
this.tvOpen.setTextColor(resources.getColor(R.color.unbind_32283C))
this.llBindInfo.visibility = View.GONE
this.tvUnbind.visibility = View.VISIBLE
}
pageAdapter.updatePages(pages)
// }else{
//
// }
this.llRoot.setOnClickListener {
Timber.d("itemClick llRoot ${item.name}, position = $position")
if (item.isBound()) {
MainActivity.start(
requireContext(),
pageType = PageType.UNBIND_PLATE,
equipmentUserInfo = item
)
} else {
MainActivity.start(
requireContext(),
pageType = PageType.BIND_PLATE,
equipmentUserInfo = item
)
}
}
this.llOpen.setOnClickListener {
Timber.d("itemClick llOpen equipmentBoxCode = ${item.equipmentBoxCode}")
val equipmentBoxCode: Int = item.equipmentBoxCode?.toInt() ?: -1
if (equipmentBoxCode < 0) {
ToastUtils.showToast("餐盘柜编号错误")
return@setOnClickListener
}
showWaitingDialog("请稍等")
SerialApi.openPlate(equipmentBoxCode, object : SerialPortManager.SendCallback {
override fun onSuccess() {
hideWaitingDialog()
MainActivity.start(requireContext(), pageType = PageType.PLATE_OPEN)
}
override fun onFail(e: Exception?) {
hideWaitingDialog()
ToastUtils.showToast("柜门打开失败")
}
})
}
}
fun updateAdapter(items: List<EquipmentUserInfo>) {
if (GlobalData.arrayCross > 2) {
var pages = if (items.size > itemsPerPage) {
items.chunked(itemsPerPage) {
convertToColumnFirst(it, 11)
}
} else {
listOf(
// 根据条件判断是否需要重新排列
// items
convertToColumnFirst(items, 11)
)
}
pageAdapter?.updatePages(pages)
} else {
itemAdapter?.updateData(items)
}
binding.mainRecyclerView.scrollToPosition(0)
}
/**
@@ -1,7 +1,6 @@
package com.sw.platecabinet.fragment
import android.os.Bundle
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.sw.inbound.utils.DateTimeUtils
import com.sw.plate.utils.ToastUtils
@@ -9,7 +8,7 @@ import com.sw.platecabinet.databinding.FragmentUnbindPlateBinding
import com.sw.platecabinet.ext.maskName
import com.sw.platecabinet.ext.maskPhone
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.viewmodel.SettingViewModel
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
/**
@@ -20,7 +19,6 @@ class UnBindPlateFragment private constructor() : BaseFragment<FragmentUnbindPla
) {
internal val ARG_PARAM1 = "param1"
private var info: EquipmentUserInfo? = null
private val viewModel by viewModels<SettingViewModel>()
companion object {
@JvmStatic
@@ -58,8 +56,9 @@ class UnBindPlateFragment private constructor() : BaseFragment<FragmentUnbindPla
}
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.bindStateChange.collect {
viewModel.bindStateChange.drop(1).collect {
if (it.first == null) return@collect // 绑定状态过滤
val errorInfo = it.second
if (errorInfo.isSuccess()) {
@@ -70,14 +69,5 @@ class UnBindPlateFragment private constructor() : BaseFragment<FragmentUnbindPla
}
}
}
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
}
}
@@ -11,16 +11,20 @@ import kotlinx.parcelize.Parcelize
*/
@Parcelize
data class BindParam(
/**
* app版本号
*/
var appVersion: String = "",
/**
* 设备ID
*/
@SerializedName("equipmentCode")
var equipmentCode: String = "",
/**
*设备盒子编号
*/
@SerializedName("equipmentBoxCode")
val equipmentBoxCode: String? = "",
/**
*设备编号
*/
@SerializedName("equipmentCode")
val equipmentCode: String? = "",
val equipmentBoxCode: String? = null,
/**
*
* 会员Id
@@ -1,6 +1,7 @@
package com.sw.platecabinet.model.request
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
/**
@@ -8,8 +9,13 @@ import kotlinx.parcelize.Parcelize
*/
@Parcelize
data class EquipmentParam(
/**
* app版本号
*/
var appVersion: String = "",
/**
* 设备ID
*/
var equipmentCode: String = ""
@SerializedName("equipmentCode")
var equipmentCode: String = "",
) : Parcelable
@@ -10,11 +10,15 @@ import kotlinx.parcelize.Parcelize
*/
@Parcelize
data class LoginParam(
/**
* app版本号
*/
var appVersion: String = "",
/**
* 设备ID
*/
@SerializedName("equipmentCode")
val equipmentCode: String? = "",
var equipmentCode: String = "",
/**
* 会员信息
*/
@@ -10,6 +10,15 @@ import kotlinx.parcelize.Parcelize
*/
@Parcelize
data class SearchParam(
/**
* app版本号
*/
var appVersion: String = "",
/**
* 设备ID
*/
@SerializedName("equipmentCode")
var equipmentCode: String = "",
@SerializedName("consumptionTime")
val consumptionTime: List<String?>? = listOf(),
@SerializedName("createTime")
@@ -3,6 +3,7 @@ package com.sw.platecabinet.model.response
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import com.sw.platecabinet.GlobalData
import kotlinx.parcelize.Parcelize
/**
@@ -50,7 +51,7 @@ data class EquipmentUserInfo(
* 餐盘编号
*/
@SerializedName("plateNumber")
val plateNumber: String? = "",
var plateNumber: String? = "",
/**
*
* 更新时间
@@ -59,7 +60,17 @@ data class EquipmentUserInfo(
val updateTime: String? = ""
) : Parcelable {
/**
* 是否已绑定
*/
fun isBound(): Boolean {
return memberId != null
}
/**
* 是否是其他设备
*/
fun isOtherEquipment(): Boolean {
return GlobalData.globalEquipmentCode != equipmentCode
}
}
@@ -5,6 +5,9 @@ import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
/**
* 搜索会员信息结果
*/
@Parcelize
data class SearchResult(
@SerializedName("endRow")
@@ -19,13 +19,20 @@ interface ApiService {
* 生成token
*/
@GET("/shuwei-zhct/scales/generateToken")
suspend fun generateToken(@Query("deviceId") deviceId: String): ApiResponse<String>
suspend fun generateToken(
@Query("deviceId") deviceId: String,
@Query("appVersion") appVersion: String,
@Query("equipmentCode") equipmentCode: String
): ApiResponse<String>
/**
* 获取人脸数据
*/
@GET("/shuwei-zhct/scales/getUserFaceCache")
suspend fun getUserFaceCache(): ApiResponse<List<UserFaceModel>>
suspend fun getUserFaceCache(
@Query("appVersion") appVersion: String,
@Query("equipmentCode") equipmentCode: String
): ApiResponse<List<UserFaceModel>>
/**
* 餐盘用户信息获取
@@ -51,4 +58,9 @@ interface ApiService {
@POST("/shuwei-user/swclientUserInfoShop/selectList")
suspend fun searchUser(@Body param: SearchParam): ApiResponse<SearchResult>
/**
* 通过餐盘号获取信息
*/
@POST("/shuwei-zhct/swEquipmentRelUser/findByPlateNumber")
suspend fun findByPlateNumber(@Body param: BindParam): ApiResponse<EquipmentUserInfo>
}
@@ -1,5 +1,6 @@
package com.sw.platecabinet.repository
import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.model.request.BindParam
import com.sw.platecabinet.model.request.EquipmentParam
import com.sw.platecabinet.model.request.LoginParam
@@ -20,14 +21,25 @@ class RemoteRepository constructor(
* 生成token
*/
suspend fun generateToken(deviceId: String): ApiResponse<String> {
return safeApiCall { apiService.generateToken(deviceId) }
return safeApiCall {
apiService.generateToken(
deviceId,
GlobalData.appVersion,
GlobalData.globalEquipmentCode
)
}
}
/**
* 获取人脸数据
*/
suspend fun getUserFaceCache(): ApiResponse<List<UserFaceModel>> {
return safeApiCall { apiService.getUserFaceCache() }
return safeApiCall {
apiService.getUserFaceCache(
GlobalData.appVersion,
GlobalData.globalEquipmentCode
)
}
}
/**
@@ -57,4 +69,11 @@ class RemoteRepository constructor(
suspend fun searchUser(param: SearchParam): ApiResponse<SearchResult> {
return safeApiCall { apiService.searchUser(param) }
}
/**
* 通过餐盘编号获取绑定信息
*/
suspend fun findByPlateNumber(param: BindParam): ApiResponse<EquipmentUserInfo> {
return safeApiCall { apiService.findByPlateNumber(param) }
}
}
@@ -102,8 +102,6 @@ object DateTimeUtils {
return when {
hours < 1 -> "刚刚"
hours < 24 -> "${hours}小时前"
// hours < 48 -> "昨天"
// hours < 72 -> "前天"
else -> {
val days = hours / 24
"${days}天前"
@@ -1,5 +1,7 @@
package com.sw.platecabinet.viewmodel
import com.sw.plate.utils.ToastUtils
import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.GlobalData.globalEquipmentCode
import com.sw.platecabinet.model.ErrorInfo
import com.sw.platecabinet.model.request.BindParam
@@ -18,6 +20,9 @@ class SettingViewModel : BaseViewModel() {
private val _searchMemberList = MutableStateFlow<List<SearchResult.Member>>(emptyList())
val searchMemberList: StateFlow<List<SearchResult.Member>> = _searchMemberList
private val _currentUserInfo = MutableStateFlow<EquipmentUserInfo?>(null)
val currentUserInfo: StateFlow<EquipmentUserInfo?> = _currentUserInfo
var currentPage = 1
var isLoading = false
var canLoadMore = true
@@ -34,10 +39,11 @@ class SettingViewModel : BaseViewModel() {
*/
fun getEquipmentList(equipmentCode: String = globalEquipmentCode) {
launchWithLoading {
val param = EquipmentParam(equipmentCode = equipmentCode)
val param =
EquipmentParam(appVersion = GlobalData.appVersion, equipmentCode = equipmentCode)
val response = repository.getEquipmentList(param)
if (parseResponse(response)) {
_equipmentList.value = response.data ?: emptyList()
_equipmentList.value = response.data?.plus(response.data) ?: emptyList()
}
}
}
@@ -49,6 +55,7 @@ class SettingViewModel : BaseViewModel() {
currentPage = pageNum
launch {
val param = SearchParam(
appVersion = GlobalData.appVersion,
param = param,
pageNum = pageNum,
pageSize = pageSize
@@ -79,6 +86,7 @@ class SettingViewModel : BaseViewModel() {
) {
launchWithLoading {
val bindParam = BindParam(
appVersion = GlobalData.appVersion,
equipmentBoxCode = equipmentBoxCode,
equipmentCode = equipmentCode,
memberId = memberId,
@@ -99,6 +107,7 @@ class SettingViewModel : BaseViewModel() {
) {
launchWithLoading {
val bindParam = BindParam(
appVersion = GlobalData.appVersion,
equipmentBoxCode = equipmentBoxCode,
equipmentCode = equipmentCode
)
@@ -110,4 +119,30 @@ class SettingViewModel : BaseViewModel() {
}
}
}
fun findByPlateNumber(
plateNumber: String,
equipmentCode: String = globalEquipmentCode
) {
launchWithLoading {
val bindParam = BindParam(
appVersion = GlobalData.appVersion,
plateNumber = plateNumber,
equipmentCode = equipmentCode
)
val response = repository.findByPlateNumber(bindParam)
if (response.code == 200) {
if (response.data?.isOtherEquipment() == true) {
ToastUtils.showToast("")
return@launchWithLoading
}
_currentUserInfo.value = response.data
} else {
_currentUserInfo.value = EquipmentUserInfo(
plateNumber = plateNumber,
equipmentCode = equipmentCode
)
}
}
}
}
@@ -7,6 +7,7 @@ import com.sw.plate.utils.Base64
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.FaceApi
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.GlobalData.globalEquipmentCode
import com.sw.platecabinet.GlobalKey
import com.sw.platecabinet.model.request.LoginParam
@@ -101,6 +102,7 @@ class UserViewModel : BaseViewModel() {
fun loginWithPwd(equipmentCode: String = globalEquipmentCode, phone: String, password: String) {
launchWithLoading {
val loginParam = LoginParam(
appVersion = GlobalData.appVersion,
equipmentCode = equipmentCode,
phone = phone,
password = password
@@ -118,6 +120,7 @@ class UserViewModel : BaseViewModel() {
fun getUserInfoById(equipmentCode: String = globalEquipmentCode, memberId: Int) {
launchWithLoading {
val loginParam = LoginParam(
appVersion = GlobalData.appVersion,
equipmentCode = equipmentCode,
memberId = memberId
)