Browse Source

refactor(更新) :
- 增加是否允许跨机柜取设备的系统参数
- 优化地图及隔离点图层显示逻辑,提升流畅度和性能
- 增加挂锁和钥匙不足时的提示文案,区分是否允许跨机柜获取设备
- 修复作业进度页面在特定情况下取还钥匙的逻辑错误
- 移除不再需要的钥匙取用状态本地存储

周文健 3 months ago
parent
commit
58d039d95b
22 changed files with 626 additions and 414 deletions
  1. 59 81
      app/src/main/java/com/grkj/iscs_mars/BusinessManager.kt
  2. 1 3
      app/src/main/java/com/grkj/iscs_mars/modbus/ModBusController.kt
  3. 5 0
      app/src/main/java/com/grkj/iscs_mars/model/DictAndSystemConstants.kt
  4. 5 0
      app/src/main/java/com/grkj/iscs_mars/model/ISCSDomainData.kt
  5. 2 0
      app/src/main/java/com/grkj/iscs_mars/model/vo/map/MapInfoRespVO.kt
  6. 21 0
      app/src/main/java/com/grkj/iscs_mars/util/IconCache.kt
  7. 1 1
      app/src/main/java/com/grkj/iscs_mars/util/NetApi.kt
  8. 0 21
      app/src/main/java/com/grkj/iscs_mars/util/SPUtils.kt
  9. 1 0
      app/src/main/java/com/grkj/iscs_mars/view/activity/HomeActivity.kt
  10. 23 25
      app/src/main/java/com/grkj/iscs_mars/view/activity/SwitchStatusActivity.kt
  11. 41 27
      app/src/main/java/com/grkj/iscs_mars/view/fragment/JobProgressFragment.kt
  12. 16 13
      app/src/main/java/com/grkj/iscs_mars/view/fragment/StepFragment.kt
  13. 42 37
      app/src/main/java/com/grkj/iscs_mars/view/fragment/WorkshopFragment.kt
  14. 32 19
      app/src/main/java/com/grkj/iscs_mars/view/presenter/HomePresenter.kt
  15. 113 45
      app/src/main/java/com/grkj/iscs_mars/view/presenter/JobProgressPresenter.kt
  16. 17 6
      app/src/main/java/com/grkj/iscs_mars/view/widget/CustomMarkLayer.kt
  17. 81 54
      app/src/main/java/com/grkj/iscs_mars/view/widget/CustomStationLayer.kt
  18. 116 73
      app/src/main/java/com/grkj/iscs_mars/view/widget/CustomSwitchStationLayer.kt
  19. 32 3
      app/src/main/java/com/onlylemi/mapview/library/MapView.java
  20. 6 2
      app/src/main/res/values-en/strings.xml
  21. 6 2
      app/src/main/res/values-zh/strings.xml
  22. 6 2
      app/src/main/res/values/strings.xml

+ 59 - 81
app/src/main/java/com/grkj/iscs_mars/BusinessManager.kt

@@ -452,7 +452,7 @@ object BusinessManager {
                 }
 
                 DeviceConst.DOCK_TYPE_COLLECT -> {
-                    ModBusController.switchStatus(bytes){}
+                    ModBusController.switchStatus(bytes) {}
                 }
             }
             Executor.delayOnMain(200) {
@@ -671,9 +671,13 @@ object BusinessManager {
             state = 1
             onConfirm.invoke()
         }
+        dlg.setOnCancelListener {
+            state = 2
+            onCancel.invoke()
+        }
         dlg.setOnDismissListener {
             if (state == 0) {
-                onConfirm.invoke()
+                onCancel.invoke()
             }
         }
         dlg.show()
@@ -825,16 +829,6 @@ object BusinessManager {
                     )
                 }
 
-                val actualLockCount = lockMap.values.sumBy { it.size }
-                // 如果锁不够,提前清空并立刻返回
-                if (actualLockCount < needLockCount) {
-                    ToastUtils.tip(
-                        MyApplication.instance!!.getString(R.string.lock_is_not_enough)
-                    )
-                    callBack(null, mutableMapOf())
-                    return@runOnMain
-                }
-
                 // —— 如果需钥匙,再请求并计算 ——
                 var keyPair: Pair<Byte, DockBean.KeyBean?>? = null
                 if (isNeedKey) {
@@ -848,11 +842,6 @@ object BusinessManager {
                                 ?.map { it.keyNfc ?: "" }?.toMutableList() ?: mutableListOf()
                         )
                     }
-                    if (keyPair == null) {
-                        ToastUtils.tip(
-                            MyApplication.instance!!.getString(R.string.no_available_key)
-                        )
-                    }
                 }
                 // —— 全部计算完毕,在主线程一次性回调 ——
                 callBack(keyPair, lockMap)
@@ -867,13 +856,6 @@ object BusinessManager {
     }
 
 
-    /**
-     * 获取开关量数据
-     */
-    fun getSwitchData(): MutableList<DockBean.SwitchBean> {
-        return ModBusController.getSwitchData()
-    }
-
     /****************************************** 蓝牙 ******************************************/
     /******************************************蓝牙通用准备******************************************/
 
@@ -983,6 +965,13 @@ object BusinessManager {
                 )
             }
         } else {
+            //如果连接在待发列表则断开连接,重新使用归还队列连接之后检查
+            if (BleSendDispatcher.isConnected(mac)) {
+                BleSendDispatcher.scheduleDisconnect(mac)
+                BleReturnDispatcher.submit(mac) {
+                    getTicketStatusBusiness(mac, isNeedLoading)
+                }
+            }
             if (isNeedLoading) sendEventMsg(
                 MsgEvent(
                     MSG_EVENT_LOADING, LoadingMsg(false, null, false)
@@ -1492,6 +1481,7 @@ object BusinessManager {
                         val keyBean = ModBusController.getKeyByMac(bleDevice.mac)
                         mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_KEY && it.nfc == keyBean?.rfid }
                             ?.let { itKey ->
+                                LogUtil.i("检查到存在待取钥匙:${itKey}")
                                 sendLoadingEventMsg(
                                     MyApplication.instance?.applicationContext!!.getString(
                                         R.string.ble_connecting
@@ -1554,9 +1544,6 @@ object BusinessManager {
                 val updateList = mutableListOf<LockPointUpdateReqVO>()
                 data.dataList?.filter { it.closed == 1 && it.status == it.target }
                     ?.forEach { dataListDTO ->
-                        data.taskCode?.toLong()?.let {
-                            SPUtils.returnKey(it)
-                        }
                         val updateVO = LockPointUpdateReqVO(
                             data.taskCode?.toLong(),
                             dataListDTO.infoRfidNo,
@@ -1570,39 +1557,20 @@ object BusinessManager {
 
                 sendLoadingEventMsg(null, false)
                 if (CAN_RETURN) {
+                    //如果需要上报的点位数据为空则直接上报钥匙归还
+                    if (updateList.isEmpty()) {
+                        mDeviceTakeList.removeIf { it.nfc == keyBean?.rfid }
+                        // 上报钥匙归还
+                        keyReturn(data.taskCode?.toLong()!!, keyNfc)
+                        switchReadyMode(bleDevice)
+                        return@forEach
+                    }
                     // 上报点位钥匙绑定
                     NetApi.updateLockPointBatch(updateList) { isSuccess, msg, code ->
                         LogUtil.i("还锁操作:${isSuccess},${msg},${code}")
                         if (isSuccess) {
                             // 上报钥匙归还
-                            NetApi.updateKeyReturn(
-                                data.taskCode?.toLong()!!,
-                                keyNfc!!,
-                                MyApplication.instance!!.serialNo()
-                            ) { isSuccess, msg, code ->
-                                if (!isSuccess && msg != MyApplication.instance?.applicationContext!!.getString(
-                                        R.string.ticket_lost
-                                    )
-                                ) {
-                                    SPUtils.saveUpdateKeyReturn(
-                                        MyApplication.instance!!,
-                                        UpdateKeyReturnBO(data.taskCode?.toLong()!!, keyNfc!!)
-                                    )
-                                    if (msg == MyApplication.instance?.applicationContext!!.getString(
-                                            R.string.ticket_lost
-                                        )
-                                    ) {
-                                        sendEventMsg(
-                                            MsgEvent(
-                                                MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null
-                                            )
-                                        )
-                                    }
-                                    ToastUtils.tip(R.string.key_return_success)
-                                } else {
-                                    ToastUtils.tip(R.string.key_return_success)
-                                }
-                            }
+                            keyReturn(data.taskCode?.toLong()!!, keyNfc)
                             data.taskCode?.toLong()?.let {
                                 sendEventMsg(
                                     MsgEvent(
@@ -1657,6 +1625,34 @@ object BusinessManager {
         }
     }
 
+    /**
+     * 钥匙归还
+     */
+    private fun keyReturn(ticketId: Long, keyNfc: String) {
+        NetApi.updateKeyReturn(
+            ticketId,
+            keyNfc,
+            MyApplication.instance!!.serialNo()
+        ) { isSuccess, msg, code ->
+            if (!isSuccess) {
+                if (msg == MyApplication.instance?.applicationContext!!.getString(
+                        R.string.ticket_lost
+                    )
+                ) {
+                    sendEventMsg(
+                        MsgEvent(
+                            MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null
+                        )
+                    )
+                }
+                SPUtils.clearUpdateKeyReturn(MyApplication.instance!!)
+                ToastUtils.tip(R.string.key_return_success)
+            } else {
+                ToastUtils.tip(R.string.key_return_success)
+            }
+        }
+    }
+
     /**
      * 处理虚拟钥匙取出,如果作业的全部点位已经上锁更新钥匙的状态使用
      */
@@ -1677,13 +1673,7 @@ object BusinessManager {
         NetApi.updateKeyReturn(
             taskCode, keyNfc, MyApplication.instance!!.serialNo()
         ) { isSuccess, msg, code ->
-            if (!isSuccess && msg != MyApplication.instance?.applicationContext!!.getString(
-                    R.string.ticket_lost
-                )
-            ) {
-                SPUtils.saveUpdateKeyReturn(
-                    MyApplication.instance!!, UpdateKeyReturnBO(taskCode, keyNfc)
-                )
+            if (!isSuccess) {
                 if (msg == MyApplication.instance?.applicationContext!!.getString(
                         R.string.ticket_lost
                     )
@@ -1694,6 +1684,7 @@ object BusinessManager {
                         )
                     )
                 }
+                SPUtils.clearUpdateKeyReturn(MyApplication.instance!!)
             } else {
                 done()
                 sendEventMsg(
@@ -1813,12 +1804,14 @@ object BusinessManager {
                             return
                         }
                         sendLoadingEventMsg(null, false)
-                        SPUtils.takeKey(info.ticketId)
                         NetApi.updateKeyTake(
                             info.ticketId, info.nfc, MyApplication.instance?.serialNo()!!
                         ) { isSuccess ->
                             if (isSuccess) {
-                                mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_KEY && it.nfc == info.nfc }
+                                mDeviceTakeList.removeIf {
+                                    it.deviceType == DEVICE_TYPE_KEY && it.nfc == info.nfc && it.ticketId ==
+                                            info.ticketId
+                                }
                                 sendEventMsg(
                                     MsgEvent(
                                         MSG_EVENT_UPDATE_TICKET_PROGRESS,
@@ -2155,14 +2148,7 @@ object BusinessManager {
                 itData.ticketId, itData.keyNfc, context.serialNo()
             ) { isSuccess, msg, code ->
                 count++
-                if (isSuccess || msg == MyApplication.instance?.applicationContext!!.getString(
-                        R.string.ticket_lost
-                    )
-                ) {
-                    returnList.remove(itData)
-                    getBleBeanByRfid(itData.keyNfc)?.bleDevice?.let {
-                        switchReadyMode(it)
-                    }
+                if (!isSuccess) {
                     if (msg == MyApplication.instance?.applicationContext!!.getString(
                             R.string.ticket_lost
                         )
@@ -2173,15 +2159,7 @@ object BusinessManager {
                             )
                         )
                     }
-                }
-                if (count == itemsToRemove.size) {
-                    if (returnList.isEmpty()) {
-                        SPUtils.clearUpdateKeyReturn(context)
-                    } else {
-                        returnList.forEach {
-                            SPUtils.saveUpdateKeyReturn(context, it)
-                        }
-                    }
+                    SPUtils.clearUpdateKeyReturn(MyApplication.instance!!)
                 }
             }
         }

+ 1 - 3
app/src/main/java/com/grkj/iscs_mars/modbus/ModBusController.kt

@@ -89,7 +89,7 @@ object ModBusController {
     fun start(ctx: Context) {
         modBusManager?.stop()
         PortManager.openCtrlBord(ctx)?.let { pm ->
-            return@let ModBusManager(pm, true)
+            return@let ModBusManager(pm, false)
         }
             // 间隔 1 秒读一遍桶的状态
             ?.repeatSendToAll(MBFrame.READ_STATUS, {
@@ -1072,7 +1072,6 @@ object ModBusController {
 
         LogUtil.i("蓝牙连接-获取到钥匙信息:${keyList}")
         if (keyList.isEmpty()) {
-            ToastUtils.tip(R.string.no_available_key)
             return null
         }
         keyList = keyList.sortedWith(
@@ -1178,7 +1177,6 @@ object ModBusController {
         LogUtil.i("异常锁仓位:${exceptionSlots.joinToString(",") { "${it.row},${it.col}" }}")
         for (lockDockIndex in lockDockList.indices) {
             if (provideCount >= needLockCount) break
-
             val validLocks =
                 lockDockList[lockDockIndex].getLockList().filter { it.rfid !in exceptionLocks }
                     .filter {

+ 5 - 0
app/src/main/java/com/grkj/iscs_mars/model/DictAndSystemConstants.kt

@@ -58,4 +58,9 @@ object DictAndSystemConstants {
      * 人脸数量限制
      */
     const val KEY_SYS_FACE_LIMIT = "sys.face.limit"
+
+    /**
+     * 是否允许跨机柜取设备
+     */
+    const val KEY_ALLOW_CROSS_CABINET = "Allow_cross_cabinet"
 }

+ 5 - 0
app/src/main/java/com/grkj/iscs_mars/model/ISCSDomainData.kt

@@ -15,4 +15,9 @@ object ISCSDomainData {
      * 是否是硬件录入
      */
     var isDeviceRegistration: Boolean = false
+
+    /**
+     * 是否允许跨机柜取设备
+     */
+    var isAllowCrossCabinet: Boolean = true
 }

+ 2 - 0
app/src/main/java/com/grkj/iscs_mars/model/vo/map/MapInfoRespVO.kt

@@ -45,5 +45,7 @@ data class MapInfoRespVO(
         val pointNfc: String?,
 
         val pointSerialNumber: String?,
+
+        val switchStatus: String?,
     )
 }

+ 21 - 0
app/src/main/java/com/grkj/iscs_mars/util/IconCache.kt

@@ -0,0 +1,21 @@
+package com.grkj.iscs_mars.util
+
+import android.graphics.Bitmap
+
+// 一个很小的位图缓存(按字节数)
+class IconCache(maxBytes: Int = 12 * 1024 * 1024) {
+    private val cache = object : android.util.LruCache<String, android.graphics.Bitmap>(maxBytes) {
+        override fun sizeOf(key: String, value: android.graphics.Bitmap): Int = value.byteCount
+    }
+    fun get(original: Bitmap, targetPx: Int): Bitmap {
+        val key = "${System.identityHashCode(original)}@$targetPx"
+        cache.get(key)?.let { return it }
+        val w = original.width
+        val h = original.height
+        val dst = if (w == targetPx && h == targetPx) original
+        else Bitmap.createScaledBitmap(original, targetPx, targetPx, true)
+        cache.put(key, dst)
+        return dst
+    }
+    fun clear() = cache.evictAll()
+}

+ 1 - 1
app/src/main/java/com/grkj/iscs_mars/util/NetApi.kt

@@ -1248,7 +1248,7 @@ object NetApi {
             { res, _, _ ->
                 res?.let {
                     callBack(getRefBean(it))
-                }
+                } ?: callBack(null)
             }, isGet = true, isAuth = true
         )
     }

+ 0 - 21
app/src/main/java/com/grkj/iscs_mars/util/SPUtils.kt

@@ -259,27 +259,6 @@ object SPUtils {
         return list.find { it.sysAttrKey == key }?.sysAttrValue
     }
 
-    /**
-     * 取出钥匙
-     */
-    fun takeKey(ticketId: Long) {
-        "${ticketId}_TakeKey".saveMMKVData(true)
-    }
-
-    /**
-     * 作业是否已经取过钥匙
-     */
-    fun isKeyTake(ticketId: Long): Boolean {
-        return "${ticketId}_TakeKey".getMMKVData(false)
-    }
-
-    /**
-     * 归还钥匙
-     */
-    fun returnKey(ticketId: Long) {
-        MMKV.defaultMMKV().remove("${ticketId}_TakeKey")
-    }
-
     /**
      * 保存作业获取锁异常状态
      */

+ 1 - 0
app/src/main/java/com/grkj/iscs_mars/view/activity/HomeActivity.kt

@@ -60,6 +60,7 @@ class HomeActivity : BaseMvpActivity<IHomeView, HomePresenter, ActivityHomeBindi
         presenter?.updateKeyData()
         presenter?.getAndSaveCabinetId()
         presenter?.checkMyTodoForHandleKey()
+        presenter?.getSysAttrs()
         val userInfo = intent.getSerializableExtra("userInfo")
 
         BusinessManager.isTestMode = false

+ 23 - 25
app/src/main/java/com/grkj/iscs_mars/view/activity/SwitchStatusActivity.kt

@@ -16,6 +16,7 @@ import com.grkj.iscs_mars.R
 import com.grkj.iscs_mars.databinding.ActivitySwitchStatusBinding
 import com.grkj.iscs_mars.databinding.ItemSwitchBinding
 import com.grkj.iscs_mars.modbus.ModBusController
+import com.grkj.iscs_mars.model.eventmsg.MsgEventConstants.MSG_EVENT_SWITCH_COLLECTION_UPDATE
 import com.grkj.iscs_mars.model.vo.map.MapInfoRespVO.IsMapPoint
 import com.grkj.iscs_mars.util.ToastUtils
 import com.grkj.iscs_mars.view.base.BaseMvpActivity
@@ -49,13 +50,6 @@ class SwitchStatusActivity :
                 mBinding?.mapview?.currentRotateDegrees = 0f
                 return super.onDoubleTap(e)
             }
-
-            override fun onFling(
-                e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float
-            ): Boolean {
-                mBinding?.mapview?.currentRotateDegrees = 0f
-                return super.onFling(e1, e2, velocityX, velocityY)
-            }
         })
         mBinding?.rvList?.linear()?.divider {
             orientation = DividerOrientation.HORIZONTAL
@@ -67,12 +61,6 @@ class SwitchStatusActivity :
             }
         }
         initMap()
-        lifecycleScope.launch {
-            repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                delay(2000)
-                mBinding?.rvList?.adapter?.notifyDataSetChanged()
-            }
-        }
     }
 
     private fun BindingAdapter.BindingViewHolder.onRVListBinding() {
@@ -83,7 +71,8 @@ class SwitchStatusActivity :
         val switchData = ModBusController.getSwitchData()
         val switchStatus = switchData
             .find { it.idx == item.pointSerialNumber?.toInt() }?.enabled
-        itemBinding.status.text = if (switchStatus == true) {
+            ?: (item.switchStatus == "1")
+        itemBinding.status.text = if (switchStatus) {
             getString(R.string.switch_open)
         } else {
             getString(R.string.switch_close)
@@ -95,6 +84,13 @@ class SwitchStatusActivity :
 
     override fun onResume() {
         super.onResume()
+        BusinessManager.mEventBus.observe(this)  {
+            when (it.code) {
+                MSG_EVENT_SWITCH_COLLECTION_UPDATE -> {
+                    mBinding?.rvList?.adapter?.notifyDataSetChanged()
+                }
+            }
+        }
         presenter?.getMapData {
             getMap(it)
         }
@@ -129,18 +125,20 @@ class SwitchStatusActivity :
         }
         mBinding?.mapview?.setMapViewListener(object : MapViewListener {
             override fun onMapLoadSuccess() {
-                if (stationLayer != null) {
-                    mBinding?.mapview?.currentRotateDegrees = 0f
-                    return
+                mBinding?.mapview?.post {
+                    if (stationLayer != null) {
+                        mBinding?.mapview?.currentRotateDegrees = 0f
+                        return@post
+                    }
+                    stationLayer = CustomSwitchStationLayer(
+                        mBinding?.mapview, presenter?.mStationList ?: mutableListOf()
+                    )
+                    mBinding?.mapview?.addLayer(stationLayer)
+                    stationLayer?.setRatio(presenter?.mapRatio ?: 1f)
+                    stationLayer?.stopAnimation()
+                    stationLayer?.startAnimation()
+                    mBinding?.mapview?.refresh()
                 }
-                stationLayer = CustomSwitchStationLayer(
-                    mBinding?.mapview, presenter?.mStationList ?: mutableListOf()
-                )
-                mBinding?.mapview?.addLayer(stationLayer)
-                stationLayer?.setRatio(presenter?.mapRatio ?: 1f)
-                stationLayer?.stopAnimation()
-                stationLayer?.startAnimation()
-                mBinding?.mapview?.refresh()
             }
 
             override fun onMapLoadFail() {

+ 41 - 27
app/src/main/java/com/grkj/iscs_mars/view/fragment/JobProgressFragment.kt

@@ -11,6 +11,7 @@ import com.grkj.iscs_mars.extentions.debounce
 import com.grkj.iscs_mars.model.bo.PageChangeBO
 import com.grkj.iscs_mars.model.eventmsg.MsgEvent
 import com.grkj.iscs_mars.model.eventmsg.MsgEventConstants
+import com.grkj.iscs_mars.model.eventmsg.MsgEventConstants.MSG_EVENT_SWITCH_COLLECTION_UPDATE
 import com.grkj.iscs_mars.model.eventmsg.MsgEventConstants.MSG_EVENT_UPDATE_TICKET_PROGRESS
 import com.grkj.iscs_mars.model.eventmsg.UpdateTicketProgressMsg
 import com.grkj.iscs_mars.model.vo.ticket.TicketDetailMonitorRespVO
@@ -106,20 +107,22 @@ class JobProgressFragment(val goBack: () -> Unit, val changePage: (PageChangeBO)
 
         mBinding?.cbAction?.debounce(2000) {
             if (mStep == 4) {
-                if (SPUtils.isKeyTake(mPageChangeBO?.ticketId!!)) {
-                    if (mTipDialog == null) {
-                        mTipDialog = TipDialog(requireContext())
-                    }
-                    mTipDialog?.setTip(getString(R.string.take_one_more_key_hint))
-                    mTipDialog?.setType(TipDialog.TYPE_HINT)
-                    mTipDialog?.setConfirmListener {
+                presenter?.getTicketDetail(mPageChangeBO?.ticketId!!) {
+                    if (it?.ticketKeyVOList?.any { it.keyStatus != "2" && it.ticketType == 0 && it.collectTime != null && it.giveBackTime == null } == true) {
+                        if (mTipDialog == null) {
+                            mTipDialog = TipDialog(requireContext())
+                        }
+                        mTipDialog?.setTip(getString(R.string.take_one_more_key_hint))
+                        mTipDialog?.setType(TipDialog.TYPE_HINT)
+                        mTipDialog?.setConfirmListener {
+                            BusinessManager.sendLoadingEventMsg(getString(R.string.system_is_processing))
+                            presenter?.handleLockProcess(mPageChangeBO?.ticketId!!)
+                        }
+                        mTipDialog?.showCancelCountdown(10)
+                    } else {
                         BusinessManager.sendLoadingEventMsg(getString(R.string.system_is_processing))
                         presenter?.handleLockProcess(mPageChangeBO?.ticketId!!)
                     }
-                    mTipDialog?.showCancelCountdown(10)
-                } else {
-                    BusinessManager.sendLoadingEventMsg(getString(R.string.system_is_processing))
-                    presenter?.handleLockProcess(mPageChangeBO?.ticketId!!)
                 }
             } else if (mStep == 7) {
                 val checkResult = presenter?.checkUnlock(requireContext(), mUserList)
@@ -148,13 +151,26 @@ class JobProgressFragment(val goBack: () -> Unit, val changePage: (PageChangeBO)
                         }
                         mTipDialog?.showCancelCountdown(10)
                     } else {
-                        if (SPUtils.isKeyTake(mPageChangeBO?.ticketId!!)) {
-                            if (mTipDialog == null) {
-                                mTipDialog = TipDialog(requireContext())
-                            }
-                            mTipDialog?.setTip(getString(R.string.take_one_more_key_hint))
-                            mTipDialog?.setType(TipDialog.TYPE_HINT)
-                            mTipDialog?.setConfirmListener {
+                        presenter?.getTicketDetail(mPageChangeBO?.ticketId!!){
+                            if (it?.ticketKeyVOList?.any { it.keyStatus != "2" && it.ticketType == 1 && it.collectTime != null && it.giveBackTime == null } == true) {
+                                if (mTipDialog == null) {
+                                    mTipDialog = TipDialog(requireContext())
+                                }
+                                mTipDialog?.setTip(getString(R.string.take_one_more_key_hint))
+                                mTipDialog?.setType(TipDialog.TYPE_HINT)
+                                mTipDialog?.setConfirmListener {
+                                    BusinessManager.sendLoadingEventMsg(getString(R.string.system_is_processing))
+                                    if (mTicketDetail?.noUnlockTicketPointsVOSet?.isNotEmpty() == true) {
+                                        mTicketDetail?.noUnlockTicketPointsVOSet?.let { noUnlockTicketPointsVOSet ->
+                                            presenter?.updateCoincideToUnLock(
+                                                noUnlockTicketPointsVOSet
+                                            )
+                                        }
+                                    }
+                                    presenter?.handleUnlockProcess(mPageChangeBO?.ticketId!!)
+                                }
+                                mTipDialog?.showCancelCountdown(10)
+                            } else {
                                 BusinessManager.sendLoadingEventMsg(getString(R.string.system_is_processing))
                                 if (mTicketDetail?.noUnlockTicketPointsVOSet?.isNotEmpty() == true) {
                                     mTicketDetail?.noUnlockTicketPointsVOSet?.let { noUnlockTicketPointsVOSet ->
@@ -163,15 +179,6 @@ class JobProgressFragment(val goBack: () -> Unit, val changePage: (PageChangeBO)
                                 }
                                 presenter?.handleUnlockProcess(mPageChangeBO?.ticketId!!)
                             }
-                            mTipDialog?.showCancelCountdown(10)
-                        } else {
-                            BusinessManager.sendLoadingEventMsg(getString(R.string.system_is_processing))
-                            if (mTicketDetail?.noUnlockTicketPointsVOSet?.isNotEmpty() == true) {
-                                mTicketDetail?.noUnlockTicketPointsVOSet?.let { noUnlockTicketPointsVOSet ->
-                                    presenter?.updateCoincideToUnLock(noUnlockTicketPointsVOSet)
-                                }
-                            }
-                            presenter?.handleUnlockProcess(mPageChangeBO?.ticketId!!)
                         }
                     }
                 } else {
@@ -216,6 +223,13 @@ class JobProgressFragment(val goBack: () -> Unit, val changePage: (PageChangeBO)
 
     override fun onResume() {
         super.onResume()
+        BusinessManager.mEventBus.observe(this) {
+            when (it.code) {
+                MSG_EVENT_SWITCH_COLLECTION_UPDATE -> {
+                    mBinding?.rvPoint?.adapter?.notifyDataSetChanged()
+                }
+            }
+        }
         isVisible = true
     }
 

+ 16 - 13
app/src/main/java/com/grkj/iscs_mars/view/fragment/StepFragment.kt

@@ -205,20 +205,23 @@ class StepFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> Uni
         }
         mBinding?.mapview?.setMapViewListener(object : MapViewListener {
             override fun onMapLoadSuccess() {
-                if (stationLayer != null) {
-                    mBinding?.mapview?.currentRotateDegrees = 0f
-                    return
-                }
-                stationLayer = CustomStationLayer(mBinding?.mapview, mStationList)
-                stationLayer?.setMarkIsClickListener(object :
-                    CustomStationLayer.MarkIsClickListener {
-                    override fun markIsClick(index: Int, btnIndex: Int) {
-//                        ToastUtils.tip(mPointList[index].name + " is selected, btnIndex is " + btnIndex)
+                // 等底图先呈现一帧
+                mBinding?.mapview?.post {
+                    if (stationLayer != null) {
+                        mBinding?.mapview?.currentRotateDegrees = 0f
+                        return@post
                     }
-                })
-                mBinding?.mapview?.addLayer(stationLayer)
-                stationLayer?.setRatio(mapRatio)
-                mBinding?.mapview?.setMinZoom(mBinding?.mapview?.currentZoom ?: 0f)
+                    stationLayer = CustomStationLayer(mBinding?.mapview, mStationList)
+                    stationLayer?.setMarkIsClickListener(object :
+                        CustomStationLayer.MarkIsClickListener {
+                        override fun markIsClick(index: Int, btnIndex: Int) {
+//                        ToastUtils.tip(mPointList[index].name + " is selected, btnIndex is " + btnIndex)
+                        }
+                    })
+                    mBinding?.mapview?.addLayer(stationLayer)
+                    stationLayer?.setRatio(mapRatio)
+                    mBinding?.mapview?.setMinZoom(mBinding?.mapview?.currentZoom ?: 0f)
+                }
             }
 
             override fun onMapLoadFail() {

+ 42 - 37
app/src/main/java/com/grkj/iscs_mars/view/fragment/WorkshopFragment.kt

@@ -168,52 +168,57 @@ class WorkshopFragment(val changePage: (PageChangeBO) -> Unit) :
         mBinding?.mapview?.isScaleAndRotateTogether = false
         mBinding?.mapview?.setMapViewListener(object : MapViewListener {
             override fun onMapLoadSuccess() {
-                // 要加null判断,否则loadMap的时候会调用onMapLoadSuccess导致多次创建layer
-                if (markLayer != null) {
-                    mBinding?.mapview?.currentRotateDegrees = 0f
-                    return
-                }
-                markLayer = CustomMarkLayer(mBinding?.mapview, mPointList)
-                markLayer?.setMarkIsClickListener(object : CustomMarkLayer.MarkIsClickListener {
-                    override fun markIsClick(index: Int, btnIndex: Int, isClickIcon: Boolean) {
+                mBinding?.mapview?.post {
+                    // 要加null判断,否则loadMap的时候会调用onMapLoadSuccess导致多次创建layer
+                    if (markLayer != null) {
+                        mBinding?.mapview?.currentRotateDegrees = 0f
+                        return@post
+                    }
+                    markLayer = CustomMarkLayer(mBinding?.mapview, mPointList)
+                    markLayer?.setMarkIsClickListener(object : CustomMarkLayer.MarkIsClickListener {
+                        override fun markIsClick(index: Int, btnIndex: Int, isClickIcon: Boolean) {
 //                        ToastUtils.tip(mPointList[index].name + " is selected, btnIndex is " + btnIndex + ", isClickIcon is " + isClickIcon)
-                        if (btnIndex == -1) {
-                            changePage(
-                                PageChangeBO(
-                                    1,
-                                    mPointList[index].workstationId,
-                                    machineryName = mPointList[index].name
-                                )
-                            )
-                        } else {
-                            if (isClickIcon && (mPointList[index].ticketList.size <= 3 || (mPointList[index].ticketList.size > 3 && btnIndex < 2))) {
+                            if (btnIndex == -1) {
                                 changePage(
                                     PageChangeBO(
-                                        2,
+                                        1,
                                         mPointList[index].workstationId,
-                                        mPointList[index].ticketList[btnIndex].ticketId,
-                                        mPointList[index].ticketList[btnIndex].machineryId,
-                                        mPointList[index].name
+                                        machineryName = mPointList[index].name
                                     )
                                 )
-                                return
-                            }
-                            TicketListDialog(requireContext()) { selectIdx ->
-                                changePage(
-                                    PageChangeBO(
-                                        2,
-                                        mPointList[index].workstationId,
-                                        mPointList[index].ticketList[selectIdx].ticketId,
-                                        mPointList[index].ticketList[selectIdx].machineryId,
-                                        mPointList[index].name
+                            } else {
+                                if (isClickIcon && (mPointList[index].ticketList.size <= 3 || (mPointList[index].ticketList.size > 3 && btnIndex < 2))) {
+                                    changePage(
+                                        PageChangeBO(
+                                            2,
+                                            mPointList[index].workstationId,
+                                            mPointList[index].ticketList[btnIndex].ticketId,
+                                            mPointList[index].ticketList[btnIndex].machineryId,
+                                            mPointList[index].name
+                                        )
+                                    )
+                                    return
+                                }
+                                TicketListDialog(requireContext()) { selectIdx ->
+                                    changePage(
+                                        PageChangeBO(
+                                            2,
+                                            mPointList[index].workstationId,
+                                            mPointList[index].ticketList[selectIdx].ticketId,
+                                            mPointList[index].ticketList[selectIdx].machineryId,
+                                            mPointList[index].name
+                                        )
                                     )
+                                }.setDataAndShow(
+                                    mPointList[index].name,
+                                    mPointList[index].ticketList
                                 )
-                            }.setDataAndShow(mPointList[index].name, mPointList[index].ticketList)
+                            }
                         }
-                    }
-                })
-                mBinding?.mapview?.addLayer(markLayer)
-                mBinding?.mapview?.refresh()
+                    })
+                    mBinding?.mapview?.addLayer(markLayer)
+                    mBinding?.mapview?.refresh()
+                }
             }
 
             override fun onMapLoadFail() {

+ 32 - 19
app/src/main/java/com/grkj/iscs_mars/view/presenter/HomePresenter.kt

@@ -10,6 +10,7 @@ import com.grkj.iscs_mars.modbus.DockBean
 import com.grkj.iscs_mars.modbus.ModBusController
 import com.grkj.iscs_mars.model.DeviceConst
 import com.grkj.iscs_mars.model.DeviceConst.DOCK_TYPE_KEY
+import com.grkj.iscs_mars.model.DictAndSystemConstants
 import com.grkj.iscs_mars.model.ISCSDomainData
 import com.grkj.iscs_mars.util.CommonUtils
 import com.grkj.iscs_mars.util.Executor
@@ -40,29 +41,32 @@ class HomePresenter : BasePresenter<IHomeView>() {
                                 keyBean.mac?.let { mac ->
                                     ThreadUtils.runOnIO {
                                         fun readJobTicket(mac: String) {
-                                                BleReturnDispatcher.submit(mac) { isConnect ->
-                                                    if (isConnect) {
-                                                        val bleBean =
-                                                            BusinessManager.getBleDeviceByMac(mac)
-                                                        Executor.delayOnMain(300) {
-                                                            bleBean?.let {
-                                                                BusinessManager.sendLoadingEventMsg(
-                                                                    mContext?.getString(
-                                                                        R.string.loading_msg_get_ticket_status_start
-                                                                    )
+                                            BleReturnDispatcher.submit(mac) { isConnect ->
+                                                if (isConnect) {
+                                                    val bleBean =
+                                                        BusinessManager.getBleDeviceByMac(mac)
+                                                    Executor.delayOnMain(300) {
+                                                        bleBean?.let {
+                                                            BusinessManager.sendLoadingEventMsg(
+                                                                mContext?.getString(
+                                                                    R.string.loading_msg_get_ticket_status_start
                                                                 )
-                                                                BusinessManager.getCurrentStatus(
-                                                                    4,
-                                                                    it.bleDevice
-                                                                )
-                                                            }
-                                                        }
-                                                    } else {
-                                                        ModBusController.controlKeyBuckle(true,mac){
-                                                            ToastUtils.tip(CommonUtils.getStr(R.string.ticket_get_failed).toString())
+                                                            )
+                                                            BusinessManager.getCurrentStatus(
+                                                                4,
+                                                                it.bleDevice
+                                                            )
                                                         }
                                                     }
+                                                } else {
+                                                    ModBusController.controlKeyBuckle(true, mac) {
+                                                        ToastUtils.tip(
+                                                            CommonUtils.getStr(R.string.ticket_get_failed)
+                                                                .toString()
+                                                        )
+                                                    }
                                                 }
+                                            }
                                         }
                                         if (!ISCSDomainData.isDeviceRegistration) {
                                             readJobTicket(mac)
@@ -136,4 +140,13 @@ class HomePresenter : BasePresenter<IHomeView>() {
             BusinessManager.checkMyTodoForHandleKey()
         }
     }
+
+    /**
+     * 获取系统配置
+     */
+    fun getSysAttrs() {
+        NetApi.getIsSystemAttributeByKey(DictAndSystemConstants.KEY_ALLOW_CROSS_CABINET) {
+            ISCSDomainData.isAllowCrossCabinet = it?.sysAttrValue == "1"
+        }
+    }
 }

+ 113 - 45
app/src/main/java/com/grkj/iscs_mars/view/presenter/JobProgressPresenter.kt

@@ -12,6 +12,7 @@ import com.grkj.iscs_mars.model.Constants.JOB_STATUS_NOT_STARTED
 import com.grkj.iscs_mars.model.Constants.USER_TYPE_LOCKER
 import com.grkj.iscs_mars.model.DeviceConst.DEVICE_TYPE_KEY
 import com.grkj.iscs_mars.model.DeviceConst.DEVICE_TYPE_LOCK
+import com.grkj.iscs_mars.model.ISCSDomainData
 import com.grkj.iscs_mars.model.eventmsg.DeviceExceptionMsg
 import com.grkj.iscs_mars.model.eventmsg.MsgEvent
 import com.grkj.iscs_mars.model.eventmsg.MsgEventConstants.MSG_EVENT_DEVICE_EXCEPTION
@@ -130,16 +131,9 @@ class JobProgressPresenter : BasePresenter<IJobProgressView>() {
                 // 3个状态全检查锁和钥匙分配情况,防止第一次拿了钥匙和部分锁
                 if (role.jobStatus == JOB_STATUS_NOT_STARTED || role.jobStatus == JOB_STATUS_ACQUIRE_LOCK || role.jobStatus == JOB_STATUS_ACQUIRE_KEY) {
                     LogUtil.i("EquipDetail : $ticketDetail")
-                    // 取锁具、取钥匙
-                    //todo 原逻辑
-//                    val needLockCount = ticketDetail.ticketLockVOList?.count { it.lockId == null }?.let { lockPointSize ->
-//                        ticketDetail.noUnlockTicketPointsVOSet?.filter { conflictData -> ticketDetail.ticketLockVOList.any { conflictData.lockId == it.lockId } }?.size?.let { conflictSize ->
-//                            lockPointSize - conflictSize
-//                        } ?: lockPointSize
-//                    } ?: 0
-                    //后端已经处理,所以直接计算即可
+                    //计算需要的锁数量
                     val needLockCount =
-                        ticketDetail.ticketPointsVOList?.count { it.lockId == null } ?: 0
+                        ticketDetail.ticketLockVOList?.count { it.lockId == null } ?: 0
                     //如果全部上锁直接返回,然后刷新界面
                     val readyLockCount =
                         BusinessManager.mDeviceTakeList.count { it.deviceType == DEVICE_TYPE_LOCK && it.ticketId == ticketId }
@@ -147,12 +141,19 @@ class JobProgressPresenter : BasePresenter<IJobProgressView>() {
                     if (readyLockCount > 0) {
                         ToastUtils.tip(R.string.please_take_out_ready_device_first)
                     }
+                    //是否需要钥匙,如果钥匙已经取出则无法再取钥匙
                     val isNeedKey =
                         ticketDetail.ticketKeyVOList?.filter { it.keyId == null }?.size != 1 &&
                                 BusinessManager.mDeviceTakeList.none { it.deviceType == DEVICE_TYPE_KEY && it.ticketId == ticketId }
                     LogUtil.i("needLockCount = $needLockCount , readyLockCount = $readyLockCount, realCount = $realCount, isNeedKey = $isNeedKey")
                     BusinessManager.checkEquipCount(realCount, isNeedKey) { keyPair, lockMap ->
-                        if (needLockCount == 0) {
+                        val lockCount = lockMap.flatMap { it.value }.count()
+                        //首先你不需要挂锁,其次你的所有点位都禁止解锁的,说明已经锁上了,直接虚拟上锁就行了,补全钥匙的数据
+                        if (needLockCount == 0 && ticketDetail.ticketPointsVOList?.map { it.pointId }
+                                ?.all {
+                                    ticketDetail.noUnlockTicketPointsVOSet?.map { it.pointId }
+                                        ?.contains(it) == true
+                                } == true) {
                             ToastUtils.tip(R.string.all_point_already_locked)
                             (keyPair?.second?.rfid ?: "VIRTUALKEY").let { keyNfc ->
                                 BusinessManager.handleVirtualKeyGive(ticketId, keyNfc) {
@@ -162,49 +163,64 @@ class JobProgressPresenter : BasePresenter<IJobProgressView>() {
                                 }
                             }
                         } else {
-                            if (lockMap.isEmpty()) {
+                            //先判断挂锁数量够不够和是否允许跨机柜取设备
+                            if (lockCount < realCount) {
                                 Executor.runOnMain {
-                                    val dialog = TipDialog(mContext!!)
-                                    dialog.setTip(mContext!!.getString(R.string.lock_is_not_enough_stop_issue_ticket))
-                                    var state = 0
-                                    dialog.setConfirmListener {
-                                        state = 1
-                                        BusinessManager.sendLoadingEventMsg(null, false)
-                                    }
-                                    dialog.setOnCancelListener {
-                                        state = 2
-                                        BusinessManager.sendLoadingEventMsg(null, false)
-                                    }
-                                    dialog.setOnDismissListener {
-                                        if (state == 0) {
-                                            BusinessManager.sendLoadingEventMsg(null, false)
+                                    if (ISCSDomainData.isAllowCrossCabinet) {
+                                        if (lockMap.isNotEmpty()) {
+                                            showTip(
+                                                mContext!!.getString(
+                                                    R.string.lock_is_not_enough_check_issue_ticket,
+                                                    lockCount
+                                                ),
+                                                TipDialog.TYPE_ALL, {
+                                                    handleGiveKey(ticketDetail, null, lockMap)
+                                                })
+                                        } else {
+                                            showTip(
+                                                mContext!!.getString(R.string.lock_is_empty_stop_issue_ticket),
+                                                TipDialog.TYPE_HINT
+                                            )
                                         }
+                                    } else {
+                                        showTip(
+                                            mContext!!.getString(R.string.lock_is_not_enough_stop_issue_ticket),
+                                            TipDialog.TYPE_HINT
+                                        )
                                     }
-                                    dialog.show()
                                 }
-                            } else if (keyPair == null) {
+                                return@checkEquipCount
+                            }
+                            //判断钥匙是否存在
+                            if (keyPair == null) {
                                 Executor.runOnMain {
-                                    val dialog = TipDialog(mContext!!)
-                                    dialog.setTip(mContext!!.getString(R.string.no_key_available_dialog_tip))
-                                    var state = 0
-                                    dialog.setConfirmListener {
-                                        state = 1
-                                        handleGiveKey(ticketDetail, null, lockMap)
-                                    }
-                                    dialog.setOnCancelListener {
-                                        state = 2
-                                        BusinessManager.sendLoadingEventMsg(null, false)
-                                    }
-                                    dialog.setOnDismissListener {
-                                        if (state == 0) {
-                                            BusinessManager.sendLoadingEventMsg(null, false)
+                                    if (ISCSDomainData.isAllowCrossCabinet) {
+                                        if (realCount == 0) {
+                                            showTip(
+                                                mContext!!.getString(R.string.key_not_enough_wait_or_take_on_another),
+                                                TipDialog.TYPE_HINT
+                                            )
+                                        } else {
+                                            showTip(
+                                                mContext!!.getString(
+                                                    R.string.lock_enough_but_key_not_enough,
+                                                    lockCount
+                                                ),
+                                                TipDialog.TYPE_ALL, {
+                                                    handleGiveKey(ticketDetail, null, lockMap)
+                                                })
                                         }
+                                    } else {
+                                        showTip(
+                                            mContext!!.getString(R.string.no_key_available_dialog_tip),
+                                            TipDialog.TYPE_HINT
+                                        )
                                     }
-                                    dialog.show()
                                 }
-                            } else {
-                                handleGiveKey(ticketDetail, keyPair, lockMap)
+                                return@checkEquipCount
                             }
+                            //条件都满足就继续操作
+                            handleGiveKey(ticketDetail, keyPair, lockMap)
                         }
                     }
                 } else {
@@ -216,6 +232,38 @@ class JobProgressPresenter : BasePresenter<IJobProgressView>() {
         }
     }
 
+    /**
+     * 显示提示
+     */
+    private fun showTip(
+        message: String,
+        dialogType: Int,
+        onConfirm: () -> Unit = {},
+        onCancel: () -> Unit = {}
+    ) {
+        val dialog = TipDialog(mContext!!)
+        dialog.setType(dialogType)
+        dialog.setTip(message)
+        var state = 0
+        dialog.setConfirmListener {
+            state = 1
+            BusinessManager.sendLoadingEventMsg(null, false)
+            onConfirm()
+        }
+        dialog.setOnCancelListener {
+            state = 2
+            BusinessManager.sendLoadingEventMsg(null, false)
+            onCancel()
+        }
+        dialog.setOnDismissListener {
+            if (state == 0) {
+                BusinessManager.sendLoadingEventMsg(null, false)
+            }
+            onCancel()
+        }
+        dialog.show()
+    }
+
     private fun handleGiveKey(
         ticketDetail: TicketDetailRespVO,
         keyPair: Pair<Byte, DockBean.KeyBean?>?,
@@ -248,7 +296,6 @@ class JobProgressPresenter : BasePresenter<IJobProgressView>() {
                 )
             )
         }
-        // null表示锁具数量不够,不给钥匙
         if (keyPair == null) {
             if (BusinessManager.mDeviceTakeList.none { it.ticketId == ticketDetail.ticketId!! }) {
                 BusinessManager.sendLoadingEventMsg(null, false)
@@ -259,6 +306,27 @@ class JobProgressPresenter : BasePresenter<IJobProgressView>() {
                 ticketDetail.ticketId!!,
                 keyPair.second?.rfid!!
             )
+            //如果挂锁都已经取了,并且点位不都是冲突点位,直接开始检查是否下发作业票
+            if (ticketDetail.ticketLockVOList?.all { it.lockId != null } == true &&
+                ticketDetail.ticketPointsVOList?.map { it.pointId }?.all {
+                    ticketDetail.noUnlockTicketPointsVOSet?.map { it.pointId }?.contains(it) == true
+                } == false
+            ) {
+                BusinessManager.getCurrentStatus(
+                    5,
+                    BusinessManager.getBleDeviceByMac(keyPair.second?.mac)!!.bleDevice
+                ) {
+                    if (!it) {
+                        return@getCurrentStatus
+                    }
+                    LogUtil.w("handleUnlockProcess timeout")
+                    BusinessManager.removeDeviceTake(
+                        DEVICE_TYPE_KEY,
+                        keyPair.second?.rfid!!
+                    )
+                    handleUnlockProcess(ticketDetail.ticketId, keyPair.second?.rfid!!)
+                }
+            }
         }
     }
 

+ 17 - 6
app/src/main/java/com/grkj/iscs_mars/view/widget/CustomMarkLayer.kt

@@ -6,6 +6,7 @@ import android.graphics.Matrix
 import android.graphics.Paint
 import android.util.Pair
 import android.view.MotionEvent
+import com.grkj.iscs_mars.util.IconCache
 import com.grkj.iscs_mars.view.fragment.WorkshopFragment.CustomPoint
 import com.onlylemi.mapview.library.MapView
 import com.onlylemi.mapview.library.layer.MapBaseLayer
@@ -29,6 +30,8 @@ class CustomMarkLayer @JvmOverloads constructor(
     private var isClickIcon: Boolean = false
     private var isFirst: Boolean = true
 
+    private val iconCache = IconCache(12 * 1024 * 1024) // 12MB 够用
+
     init {
         num = -1
         btnIndex = -1
@@ -40,6 +43,7 @@ class CustomMarkLayer @JvmOverloads constructor(
         paint = Paint()
         paint.isAntiAlias = true
         paint.style = Paint.Style.FILL_AND_STROKE
+        paint.isFilterBitmap = true
     }
 
     override fun onTouch(event: MotionEvent) {
@@ -144,10 +148,16 @@ class CustomMarkLayer @JvmOverloads constructor(
         if (isVisible) {
             canvas.save()
             if (pointList.isNotEmpty()) {
+                val viewW = canvas.width.toFloat()
+                val viewH = canvas.height.toFloat()
+                val margin = mBitmapSize * 3f
+                val viewport = android.graphics.RectF(-margin, -margin, viewW + margin, viewH + margin)
+
                 pointList.forEach { point ->
                     val mark = point.pos
                     val goal = floatArrayOf(mark.x, mark.y)
                     currentMatrix.mapPoints(goal)
+                    if (!viewport.contains(goal[0], goal[1])) return@forEach  // ← 屏外跳过
 
                     // 文字背景
                     paint.textSize = radiusMark
@@ -194,12 +204,13 @@ class CustomMarkLayer @JvmOverloads constructor(
                             val ticketY = goal[1] - mBitmapSize / 2 * 3
 
                             // 绘制图标
-                            canvas.drawBitmap(
-                                if (point.ticketList.size > 3 && j == 2) point.ticketList[j + 1].bitmap!! else list[j].bitmap!!,
-                                ticketX,
-                                ticketY,
-                                paint
-                            )
+                            val srcBm = if (point.ticketList.size > 3 && j == 2)
+                                point.ticketList[j + 1].bitmap!!
+                            else
+                                list[j].bitmap!!
+
+                            val iconBm = iconCache.get(srcBm, mBitmapSize) // ← 关键:先缩放到 60px
+                            canvas.drawBitmap(iconBm, ticketX, ticketY, paint)
 
                             // 计算序号背景的位置和大小
                             val numberText =

+ 81 - 54
app/src/main/java/com/grkj/iscs_mars/view/widget/CustomStationLayer.kt

@@ -43,6 +43,7 @@ class CustomStationLayer @JvmOverloads constructor(
 
     fun setRatio(ratio: Float) {
         this.ratio = ratio
+        bgBitmap?.recycle(); coverBitmap?.recycle()
         bgBitmap = BitmapUtil.getResizedBitmapFromDrawable(
             mapView.context,
             R.drawable.red_stroke_bg,
@@ -65,73 +66,99 @@ class CustomStationLayer @JvmOverloads constructor(
     override fun draw(
         canvas: Canvas, currentMatrix: Matrix, currentZoom: Float, currentRotateDegrees: Float
     ) {
+        if (!isVisible) return
         if (inDraw) {
             return
         }
         inDraw = true
         this.currentZoom = currentZoom
         currentDegree = 360 - currentRotateDegrees
-
-        if (!isVisible) return
-
-        canvas.save()
-        // 把 mapView 本身的缩放/平移/旋转一次性 concat 到 Canvas
-        canvas.concat(currentMatrix)
-        val switchData = ModBusController.getSwitchData()
-        val tempPointList = pointList.toList()
-        tempPointList.forEach { point ->
-            val switchStatus = switchData
-                .find { it.idx == point.pointSerialNumber?.toInt() }?.enabled
-            // point.pos.x/y 已经是「图内像素坐标」
-            val x = point.pos.x
-            val y = point.pos.y
-            // 先画背景(它会被 currentMatrix 自动缩放)
-            bgBitmap?.let {
-                canvas.drawBitmap(
-                    it, x, y, paint
-                )
-                point.icon?.let { icon ->
-                    canvas.drawBitmap(
-                        icon,
-                        x + (it.width - icon.width) / 2,
-                        y + (it.width - icon.width) / 2,
-                        paint
-                    )
-                }
-                // 然后画文字
-                paint.style = Paint.Style.FILL
-                paint.strokeWidth = 1f
-                paint.color = Color.RED
-                paint.textSize = radiusMark * ratio  // 这里是「图内」的文字大小,后面会跟着缩放
-                val textW = paint.measureText(point.entityName)
-                canvas.drawText(
-                    point.entityName,
-                    x + (it.width - textW) / 2,
-                    y + (it.height - radiusMark / 2 - it.height / 10),
-                    paint
-                )
-
-                // 如果选中,再叠加一个标记
-                if (point.isSelected) {
-                    coverBitmap?.let {
+        try {
+            canvas.save()
+            try {
+                // 把 mapView 本身的缩放/平移/旋转一次性 concat 到 Canvas
+                canvas.concat(currentMatrix)
+                val switchData = ModBusController.getSwitchData()
+                val tempPointList = pointList.toList()
+                val inv = Matrix().apply { currentMatrix.invert(this) }
+                val screen = android.graphics.RectF(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat())
+                inv.mapRect(screen) // screen 现在是“图内坐标系下”的可见区域
+
+                for (point in tempPointList) {
+                    // point.pos.x/y 已经是「图内像素坐标」
+                    val x = point.pos.x
+                    val y = point.pos.y
+
+                    val bw = bgBitmap?.width ?: 0
+                    val bh = bgBitmap?.height ?: 0
+                    if (!screen.intersects(x, y, x + bw, y + bh)) continue  // ← 屏外跳过
+                    // 先画背景(它会被 currentMatrix 自动缩放)
+                    bgBitmap?.let {bg->
                         canvas.drawBitmap(
-                            it, x, y, paint
+                            bg, x, y, paint
                         )
+                        point.icon?.let { raw ->
+                            // 背景里要放图标的“内容槽”大小(自己按 bg 的留白算,这里举例居中正方形)
+                            val slot = minOf(bg.width, bg.height) * 0.6f
+                            val w = slot.toInt()
+                            val h = w
+                            val icon = getScaledIcon(raw, w, h)   // ← 关键:先缩放再画
+                            val dx = x + (bg.width - w) / 2f
+                            val dy = y + (bg.height - h) / 2f
+                            paint.isFilterBitmap = true
+                            paint.isDither = true
+                            canvas.drawBitmap(icon, dx, dy, paint)
+                        }
+                        // 然后画文字
+                        paint.style = Paint.Style.FILL
+                        paint.strokeWidth = 1f
+                        paint.color = Color.RED
+                        paint.textSize = radiusMark * ratio  // 这里是「图内」的文字大小,后面会跟着缩放
+                        val textW = paint.measureText(point.entityName)
+                        canvas.drawText(
+                            point.entityName,
+                            x + (bg.width - textW) / 2,
+                            y + (bg.height - radiusMark / 2 - bg.height / 10),
+                            paint
+                        )
+
+                        // 如果选中,再叠加一个标记
+                        if (point.isSelected) {
+                            coverBitmap?.let {
+                                canvas.drawBitmap(
+                                    it, x, y, paint
+                                )
+                            }
+                            val checkW = paint.measureText("√")
+                            paint.color = Color.WHITE
+                            canvas.drawText(
+                                "√",
+                                x + (bg.width - checkW) / 2,
+                                y + (bg.height / 2 + radiusMark / 2),
+                                paint
+                            )
+                        }
                     }
-                    val checkW = paint.measureText("√")
-                    paint.color = Color.WHITE
-                    canvas.drawText(
-                        "√",
-                        x + (it.width - checkW) / 2,
-                        y + (it.height / 2 + radiusMark / 2),
-                        paint
-                    )
                 }
+            } finally {
+                canvas.restore()
             }
+        } finally {
+            inDraw = false
         }
+    }
+
+    private val iconCache = object : android.util.LruCache<String, Bitmap>(12 * 1024 * 1024) {
+        override fun sizeOf(key: String, value: Bitmap) = value.byteCount
+    }
 
-        canvas.restore()
-        inDraw = false
+    private fun getScaledIcon(src: Bitmap, targetW: Int, targetH: Int): Bitmap {
+        val key = "${System.identityHashCode(src)}_${targetW}x${targetH}"
+        iconCache.get(key)?.let { return it }
+        val out = if (src.width == targetW && src.height == targetH) src
+        else Bitmap.createScaledBitmap(src, targetW, targetH, true)
+        iconCache.put(key, out)
+        return out
     }
 
     private fun rotatePoint(

+ 116 - 73
app/src/main/java/com/grkj/iscs_mars/view/widget/CustomSwitchStationLayer.kt

@@ -52,6 +52,7 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
     private var ratio: Float = 1f
     private var switchSize: Float = 1f
     private var textSize: Float = 1f
+    private var lastKnownZoom: Float = 1f
 
     init {
         initLayer()
@@ -119,96 +120,137 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
     }
 
     override fun onTouch(event: MotionEvent) {
+        if (event.action != MotionEvent.ACTION_UP) return
 
+        // 屏幕坐标 → 图内坐标(和 draw() 的 concat 对应)
+        val inv = Matrix()
+        val m = Matrix(mapView.currentMatrix)
+        if (!m.invert(inv)) return
+        val pt = floatArrayOf(event.x, event.y)
+        inv.mapPoints(pt)
+        val mapX = pt[0];
+        val mapY = pt[1]
+
+        // 命中半径(用和绘制一致的图内半径,稍放宽)
+        val hitR = switchSize * 1.2f
+        val hitR2 = hitR * hitR
+
+        val snapshot = synchronized(stationList) { stationList.toList() }
+        var hit: IsolationPoint? = null
+        var bestD2 = Float.MAX_VALUE
+        for (p in snapshot) {
+            val dx = mapX - p.pos.x
+            val dy = mapY - p.pos.y
+            val d2 = dx * dx + dy * dy
+            if (d2 <= hitR2 && d2 < bestD2) {
+                bestD2 = d2
+                hit = p
+            }
+        }
+
+        // 命中了才操作:用 pointSerialNumber 走你现成的 selectPoint;scale 用当前缩放
+        hit?.pointSerialNumber?.let { serial ->
+            selectPoint(
+                serial = serial,
+                animated = true,
+                durationMs = 300L,
+                scale = lastKnownZoom   // 不改缩放,只居中
+            )
+        }
     }
 
     override fun draw(
         canvas: Canvas, currentMatrix: Matrix, currentZoom: Float, currentRotateDegrees: Float
     ) {
+        if (!isVisible) return  // ← 只有这一个早退允许在 save 之前
         if (inDraw) {
             return
         }
         inDraw = true
         this.currentZoom = currentZoom
+        lastKnownZoom = currentZoom   // ← 记录下来
         currentDegree = 360 - currentRotateDegrees
+        try {
+            canvas.save()
+            try {
+// 把 mapView 本身的缩放/平移/旋转一次性 concat 到 Canvas
+                canvas.concat(currentMatrix)
+                val switchData = ModBusController.getSwitchData()
+                val tempPointList = synchronized(stationList) { stationList.toList() }
+                tempPointList.forEach { point ->
+                    val switchStatus = switchData
+                        .find { it.idx == point.pointSerialNumber?.toInt() }?.enabled
+                    // point.pos.x/y 已经是「图内像素坐标」
+                    val x = point.pos.x
+                    val y = point.pos.y
+                    // 先画背景(它会被 currentMatrix 自动缩放)
+                    paint.alpha = 255
+                    if (switchStatus != null) {
+                        if (point.isSelected) {
+                            paint.color = Color.RED
+                            paint.strokeWidth = 2f
+                            paint.style = Paint.Style.STROKE
+                            canvas.drawRect(
+                                x - switchSize / 2 - paint.strokeWidth,
+                                y - switchSize / 2 - paint.strokeWidth,
+                                x + switchSize * 1.5f + paint.strokeWidth,
+                                y + switchSize * 1.5f + paint.strokeWidth,
+                                paint
+                            )
+                        }
+                        paint.style = Paint.Style.FILL_AND_STROKE
+                        // 再画 icon
+                        if (switchStatus) {
+                            paint.color = ContextCompat.getColor(
+                                MyApplication.instance?.applicationContext!!,
+                                R.color.common_switch_enable
+                            )
+                            paint.alpha = alpha
+                            canvas.drawCircle(
+                                x + switchSize / 2,
+                                y + switchSize / 2,
+                                switchSize, paint
+                            )
+                            paint.alpha = 255
+                        } else {
+                            paint.color = ContextCompat.getColor(
+                                MyApplication.instance?.applicationContext!!,
+                                R.color.common_switch_disable
+                            )
+                            canvas.drawCircle(
+                                x + switchSize / 2,
+                                y + switchSize / 2,
+                                switchSize, paint
+                            )
+                        }
+                    }
+                    // 然后画文字
+                    paint.style = Paint.Style.FILL
+                    paint.strokeWidth = 1f
+                    paint.color = Color.WHITE
+                    paint.textSize = textSize  // 这里是「图内」的文字大小,后面会跟着缩放
+                    val textW = paint.measureText(point.pointSerialNumber ?: "")
+                    // 计算文本宽度和偏移
+                    val fontMetrics = paint.fontMetrics
+                    val textHeight = fontMetrics.bottom - fontMetrics.top
 
-        if (!isVisible) return
-
-        canvas.save()
-        // 把 mapView 本身的缩放/平移/旋转一次性 concat 到 Canvas
-        canvas.concat(currentMatrix)
-        val switchData = ModBusController.getSwitchData()
-        val tempPointList = stationList.toList()
-        tempPointList.forEach { point ->
-            val switchStatus = switchData
-                .find { it.idx == point.pointSerialNumber?.toInt() }?.enabled
-            // point.pos.x/y 已经是「图内像素坐标」
-            val x = point.pos.x
-            val y = point.pos.y
-            // 先画背景(它会被 currentMatrix 自动缩放)
-            paint.alpha = 255
-            if (switchStatus != null) {
-                if (point.isSelected) {
-                    paint.color = Color.RED
-                    paint.strokeWidth = 2f
-                    paint.style = Paint.Style.STROKE
-                    canvas.drawRect(
-                        x - switchSize / 2 - paint.strokeWidth,
-                        y - switchSize / 2 - paint.strokeWidth,
-                        x + switchSize * 1.5f + paint.strokeWidth,
-                        y + switchSize * 1.5f + paint.strokeWidth,
+                    // 计算绘制文本的起点(左下角)
+                    val textX = x + (switchSize - textW) / 2
+                    val textY = y + switchSize / 2 + (textHeight / 2 - fontMetrics.bottom)
+                    canvas.drawText(
+                        "${point.pointSerialNumber}",
+                        textX,
+                        textY,
                         paint
                     )
                 }
-                paint.style = Paint.Style.FILL_AND_STROKE
-                // 再画 icon
-                if (switchStatus) {
-                    paint.color = ContextCompat.getColor(
-                        MyApplication.instance?.applicationContext!!,
-                        R.color.common_switch_enable
-                    )
-                    paint.alpha = alpha
-                    canvas.drawCircle(
-                        x + switchSize / 2,
-                        y + switchSize / 2,
-                        switchSize, paint
-                    )
-                    paint.alpha = 255
-                } else {
-                    paint.color = ContextCompat.getColor(
-                        MyApplication.instance?.applicationContext!!,
-                        R.color.common_switch_disable
-                    )
-                    canvas.drawCircle(
-                        x + switchSize / 2,
-                        y + switchSize / 2,
-                        switchSize, paint
-                    )
-                }
+            } finally {
+                canvas.restore()
             }
-            // 然后画文字
-            paint.style = Paint.Style.FILL
-            paint.strokeWidth = 1f
-            paint.color = Color.WHITE
-            paint.textSize = textSize  // 这里是「图内」的文字大小,后面会跟着缩放
-            val textW = paint.measureText(point.pointSerialNumber ?: "")
-            // 计算文本宽度和偏移
-            val fontMetrics = paint.fontMetrics
-            val textHeight = fontMetrics.bottom - fontMetrics.top
-
-            // 计算绘制文本的起点(左下角)
-            val textX = x + (switchSize - textW) / 2
-            val textY = y + switchSize / 2 + (textHeight / 2 - fontMetrics.bottom)
-            canvas.drawText(
-                "${point.pointSerialNumber}",
-                textX,
-                textY,
-                paint
-            )
+        } finally {
+            inDraw = false
         }
 
-        canvas.restore()
-        inDraw = false
     }
 
     /**
@@ -231,7 +273,8 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
     fun selectPoint(
         serial: String,
         animated: Boolean = true,
-        durationMs: Long = 300L
+        durationMs: Long = 300L,
+        scale: Float = 2f
     ): Boolean {
         val point = stationList.find { it.pointSerialNumber == serial } ?: return false
         point.isSelected = true
@@ -251,7 +294,7 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
         // 2. 定位:调用 MapView 的 API
         mapView?.let { mv ->
             if (animated) {
-                mv.animateCenterOnPoint(point.pos.x, point.pos.y, durationMs)
+                mv.animateCenterOnPoint(point.pos.x, point.pos.y, durationMs, scale)
             } else {
                 mv.mapCenterWithPoint(point.pos.x, point.pos.y)
             }

+ 32 - 3
app/src/main/java/com/onlylemi/mapview/library/MapView.java

@@ -101,7 +101,9 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
     }
 
     public void loadMap(Bitmap bitmap) {
-        loadMap(MapUtils.getPictureFromBitmap(bitmap));
+        Bitmap safe = ensureSafeBitmapSize(bitmap);
+        loadMap(MapUtils.getPictureFromBitmap(safe));
+        if (safe != bitmap) bitmap.recycle();
     }
 
     public void loadMap(final Picture picture) {
@@ -134,6 +136,16 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
         }).start();
     }
 
+    private Bitmap ensureSafeBitmapSize(Bitmap src) {
+        final int GL_SAFE_MAX = 4096;  // 保守一点
+        final int max = Math.max(src.getWidth(), src.getHeight());
+        if (max <= GL_SAFE_MAX) return src;
+        float scale = GL_SAFE_MAX / (float) max;
+        int w = Math.round(src.getWidth() * scale);
+        int h = Math.round(src.getHeight() * scale);
+        return Bitmap.createScaledBitmap(src, w, h, true);
+    }
+
     public void refresh() {
         if (surface == null || !isMapLoadFinish) return;
         Canvas canvas = lockCanvas();
@@ -247,6 +259,14 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
     }
 
     public void setCurrentRotateDegrees(float degrees) {
+        final float px = getWidth() / 2f;
+        final float py = getHeight() / 2f;
+        if (currentZoom != minZoom) {
+            float s = minZoom / currentZoom;     // 相对缩放因子
+            currentMatrix.postScale(s, s, px, py);
+            currentZoom = minZoom;
+        }
+        currentMatrix.postRotate(degrees - currentRotateDegrees, getWidth() / 2f, getHeight() / 2f);
         mapCenterWithPoint(
                 mapLayer != null ? mapLayer.getImage().getWidth() / 2f : 0f,
                 mapLayer != null ? mapLayer.getImage().getHeight() / 2f : 0f
@@ -342,13 +362,16 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
      * @param x        地图坐标 x
      * @param y        地图坐标 y
      * @param duration 动画时长(ms),建议 200~500
+     * @param scale    缩放等级
      */
-    public void animateCenterOnPoint(final float x, final float y, long duration) {
+    public void animateCenterOnPoint(final float x, final float y, long duration, float scale) {
         // 1. 计算目标偏移
-        float[] pts = { x, y };
+        float[] pts = {x, y};
         currentMatrix.mapPoints(pts);
         final float targetDx = getWidth() / 2f - pts[0];
         final float targetDy = getHeight() / 2f - pts[1];
+        final float startZoom = currentZoom;
+        final float targetZoom = scale; // 终极目标缩放
 
         // 2. 记录初始矩阵
         final Matrix startMatrix = new Matrix(currentMatrix);
@@ -359,8 +382,14 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
         animator.setInterpolator(new AccelerateDecelerateInterpolator());
         animator.addUpdateListener(animation -> {
             float frac = (float) animation.getAnimatedValue();
+            // 当前帧缩放值(线性插值)
+            float currentScale = startZoom + (targetZoom - startZoom) * frac;
             currentMatrix.set(startMatrix);
+            // 围绕目标点进行缩放
+            currentMatrix.postScale(currentScale / startZoom, currentScale / startZoom, x, y);
             currentMatrix.postTranslate(targetDx * frac, targetDy * frac);
+            // 更新成员变量
+            currentZoom = currentScale;
             refresh();
         });
         animator.start();

+ 6 - 2
app/src/main/res/values-en/strings.xml

@@ -291,7 +291,7 @@
     <string name="use_default_cabinet_id">Will use default CabinetId</string>
     <string name="please_set_url">Please enter URL, empty will use default</string>
     <string name="url_format_error">Please start with http:// or https://</string>
-    <string name="no_key_available_dialog_tip">No available keys, confirm to continue permit?</string>
+    <string name="no_key_available_dialog_tip">The current cabinet key is insufficient, please wait for other operations to return the key.</string>
     <string name="lock_take_report_fail">Failed to report padlock removal</string>
     <string name="board_address_exists">Board address already exists</string>
     <string name="row">Row</string>
@@ -339,7 +339,7 @@
     <string name="current_user_does_not_have_the_authority_to_colock">The current user does not have the authority to colock</string>
     <string name="current_user_does_not_have_the_authority_to_unlock_colock">The current user does not have the authority to unlock colock</string>
     <string name="all_point_have_other_job_not_finish">all point have other job not finish,do you want to continue?</string>
-    <string name="lock_is_not_enough_stop_issue_ticket">lock is not enough, stop issue ticket</string>
+    <string name="lock_is_not_enough_stop_issue_ticket">The current cabinet padlock is insufficient, please wait for other jobs to return the padlock.</string>
     <string name="ticket_lost">作业票数据丢失啦!</string>
     <string name="current_ticket_report_lock_take_exception_tip">current ticket report lock take exception, please return lock</string>
     <string name="please_input_exception_reason">please input exception reason</string>
@@ -400,4 +400,8 @@
     <string name="operation">Operation</string>
     <string name="ticket_get_failed">Ticket get failed,Please reposition the key</string>
     <string name="ticket_not_finish_can_not_return_lock">The padlock is not allowed to be returned if the work ticket is not completed</string>
+    <string name="lock_is_not_enough_check_issue_ticket">%1$d padlocks can be distributed to the current cabinet. Please obtain the remaining padlocks from other cabinets or wait for other operations to return Padlock, are you sure to perform operation?</string>
+    <string name="lock_is_empty_stop_issue_ticket">There is no padlock available for the current cabinet. Please obtain the padlock from other cabinets or wait for other jobs to return the padlock.</string>
+    <string name="lock_enough_but_key_not_enough">The current cabinet can distribute %1$d padlocks. Please obtain keys from other cabinets or wait for other jobs to return the keys. Are you sure you want to perform the operation?</string>
+    <string name="key_not_enough_wait_or_take_on_another">There is no key available for distribution in the current cabinet. Please obtain the key from other cabinets or wait for other operations to return the key.</string>
 </resources>

+ 6 - 2
app/src/main/res/values-zh/strings.xml

@@ -291,7 +291,7 @@
     <string name="use_default_cabinet_id">将使用默认锁柜id</string>
     <string name="please_set_url">请输入地址,清空保存将使用默认地址</string>
     <string name="url_format_error">请以http://或https://开头</string>
-    <string name="no_key_available_dialog_tip">暂无可用钥匙,确认继续执行作业票吗?</string>
+    <string name="no_key_available_dialog_tip">当前机柜钥匙不足,请等待其他作业归还钥匙。</string>
     <string name="lock_take_report_fail">挂锁取出上报失败</string>
     <string name="board_address_exists">存在相同地址的主板</string>
     <string name="row">行</string>
@@ -339,7 +339,7 @@
     <string name="current_user_does_not_have_the_authority_to_colock">当前用户无权共锁</string>
     <string name="current_user_does_not_have_the_authority_to_unlock_colock">当前用户无权解除共锁</string>
     <string name="all_point_have_other_job_not_finish">所有隔离点存在其他作业未完成,是否继续拆锁恢复?</string>
-    <string name="lock_is_not_enough_stop_issue_ticket">锁具数量不足,停止下发作业票</string>
+    <string name="lock_is_not_enough_stop_issue_ticket">当前机柜挂锁不足,请等待其他作业归还挂锁。</string>
     <string name="ticket_lost">作业票数据丢失啦!</string>
     <string name="current_ticket_report_lock_take_exception_tip">当前作业挂锁上报异常,请归还挂锁</string>
     <string name="please_input_exception_reason">请输入异常原因</string>
@@ -400,4 +400,8 @@
     <string name="operation">操作</string>
     <string name="ticket_get_failed">作业票获取失败,请重新放置钥匙</string>
     <string name="ticket_not_finish_can_not_return_lock">作业票未完成,禁止归还挂锁</string>
+    <string name="lock_is_not_enough_check_issue_ticket">当前机柜可派发%1$d把挂锁,剩余挂锁请从其他机柜获取或等待其他作业归还挂锁\n确定要执⾏操作吗?</string>
+    <string name="lock_is_empty_stop_issue_ticket">当前机柜无可派发挂锁,请从其他机柜获取挂锁或等待其他作业归还挂锁。</string>
+    <string name="lock_enough_but_key_not_enough">当前机柜可派发%1$d把挂锁,请从其他机柜获取钥匙或等待其他作业归还钥匙\n确定要执行操作吗?</string>
+    <string name="key_not_enough_wait_or_take_on_another">当前机柜无可派发钥匙,请从其他机柜获取或等待其他作业归还钥匙。</string>
 </resources>

+ 6 - 2
app/src/main/res/values/strings.xml

@@ -291,7 +291,7 @@
     <string name="use_default_cabinet_id">将使用锁柜id</string>
     <string name="please_set_url">请输入地址,清空保存将使用默认地址</string>
     <string name="url_format_error">请以http://或https://开头</string>
-    <string name="no_key_available_dialog_tip">暂无可用钥匙,确认继续执行作业票吗?</string>
+    <string name="no_key_available_dialog_tip">当前机柜钥匙不足,请等待其他作业归还钥匙。</string>
     <string name="lock_take_report_fail">挂锁取出上报失败</string>
     <string name="board_address_exists">存在相同地址的主板</string>
     <string name="row">行</string>
@@ -339,7 +339,7 @@
     <string name="current_user_does_not_have_the_authority_to_colock">当前用户无权共锁</string>
     <string name="current_user_does_not_have_the_authority_to_unlock_colock">当前用户无权解除共锁</string>
     <string name="all_point_have_other_job_not_finish">所有隔离点存在其他作业未完成,是否继续拆锁恢复?</string>
-    <string name="lock_is_not_enough_stop_issue_ticket">锁具数量不足,停止下发作业票</string>
+    <string name="lock_is_not_enough_stop_issue_ticket">当前机柜挂锁不足,请等待其他作业归还挂锁。</string>
     <string name="ticket_lost">作业票数据丢失啦!</string>
     <string name="current_ticket_report_lock_take_exception_tip">当前作业挂锁上报异常,请归还挂锁</string>
     <string name="please_input_exception_reason">请输入异常原因</string>
@@ -400,4 +400,8 @@
     <string name="operation">操作</string>
     <string name="ticket_get_failed">作业票获取失败,请重新放置钥匙</string>
     <string name="ticket_not_finish_can_not_return_lock">作业票未完成,禁止归还挂锁</string>
+    <string name="lock_is_not_enough_check_issue_ticket">当前机柜可派发%1$d把挂锁,剩余挂锁请从其他机柜获取或等待其他作业归还挂锁\n确定要执⾏操作吗?</string>
+    <string name="lock_is_empty_stop_issue_ticket">当前机柜无可派发挂锁,请从其他机柜获取挂锁或等待其他作业归还挂锁。</string>
+    <string name="lock_enough_but_key_not_enough">当前机柜可派发%1$d把挂锁,请从其他机柜获取钥匙或等待其他作业归还钥匙\n确定要执行操作吗?</string>
+    <string name="key_not_enough_wait_or_take_on_another">当前机柜无可派发钥匙,请从其他机柜获取或等待其他作业归还钥匙。</string>
 </resources>