feat: 搜索页支持JSON扫码直达,去掉前缀提示
- 搜索输入框内容若是JSON则解码,type为ops_sys时根据module跳转(workorder→工单详情,item→物品详情) - JSON解码后无论type是否为ops_sys都清空输入框 - 非JSON内容走原有关键字搜索逻辑 - 去掉前缀快捷搜索提示文本
This commit is contained in:
@@ -111,7 +111,13 @@ fun OPSNavGraph(isLoggedIn: Boolean) {
|
|||||||
onWorkOrderClick = { id -> rootNavController.navigate(Routes.workOrderDetail(id)) },
|
onWorkOrderClick = { id -> rootNavController.navigate(Routes.workOrderDetail(id)) },
|
||||||
onItemClick = { id -> rootNavController.navigate(Routes.itemDetail(id)) },
|
onItemClick = { id -> rootNavController.navigate(Routes.itemDetail(id)) },
|
||||||
onContainerClick = { id -> rootNavController.navigate(Routes.MAIN) },
|
onContainerClick = { id -> rootNavController.navigate(Routes.MAIN) },
|
||||||
onBack = { rootNavController.popBackStack() }
|
onBack = { rootNavController.popBackStack() },
|
||||||
|
onOpsSysNavigate = { module, id ->
|
||||||
|
when (module) {
|
||||||
|
"workorder" -> rootNavController.navigate(Routes.workOrderDetail(id))
|
||||||
|
"item" -> rootNavController.navigate(Routes.itemDetail(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(Routes.SCHEDULE) {
|
composable(Routes.SCHEDULE) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
@@ -51,7 +51,8 @@ fun SearchScreen(
|
|||||||
onWorkOrderClick: (Int) -> Unit,
|
onWorkOrderClick: (Int) -> Unit,
|
||||||
onItemClick: (Int) -> Unit,
|
onItemClick: (Int) -> Unit,
|
||||||
onContainerClick: (Int) -> Unit,
|
onContainerClick: (Int) -> Unit,
|
||||||
onBack: () -> Unit
|
onBack: () -> Unit,
|
||||||
|
onOpsSysNavigate: (module: String, id: Int) -> Unit
|
||||||
) {
|
) {
|
||||||
val appContainer = LocalAppContainer.current
|
val appContainer = LocalAppContainer.current
|
||||||
val viewModel: SearchViewModel = viewModel(
|
val viewModel: SearchViewModel = viewModel(
|
||||||
@@ -67,6 +68,13 @@ fun SearchScreen(
|
|||||||
if (granted) showScanner = true
|
if (granted) showScanner = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(uiState.opsSysNavigation) {
|
||||||
|
uiState.opsSysNavigation?.let { navInfo ->
|
||||||
|
onOpsSysNavigate(navInfo.module, navInfo.id)
|
||||||
|
viewModel.clearOpsSysNavigation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun checkCameraAndScan() {
|
fun checkCameraAndScan() {
|
||||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||||
hasCameraPermission = true
|
hasCameraPermission = true
|
||||||
@@ -128,14 +136,6 @@ fun SearchScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefix hints
|
|
||||||
Text(
|
|
||||||
"前缀快捷搜索: po:采购单 wo:工单 item:物品 warehouse:容器",
|
|
||||||
fontSize = 11.sp,
|
|
||||||
color = Color.Gray,
|
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
|
|
||||||
// Category tabs
|
// Category tabs
|
||||||
PrimaryScrollableTabRow(
|
PrimaryScrollableTabRow(
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import com.example.ops_android.data.model.Container
|
|||||||
import com.example.ops_android.data.model.Item
|
import com.example.ops_android.data.model.Item
|
||||||
import com.example.ops_android.data.model.PurchaseOrder
|
import com.example.ops_android.data.model.PurchaseOrder
|
||||||
import com.example.ops_android.data.model.WorkOrder
|
import com.example.ops_android.data.model.WorkOrder
|
||||||
import com.example.ops_android.data.repository.CustomerRepository
|
|
||||||
import com.example.ops_android.data.repository.OrderRepository
|
import com.example.ops_android.data.repository.OrderRepository
|
||||||
import com.example.ops_android.data.repository.WarehouseRepository
|
import com.example.ops_android.data.repository.WarehouseRepository
|
||||||
import com.example.ops_android.data.repository.WorkOrderRepository
|
import com.example.ops_android.data.repository.WorkOrderRepository
|
||||||
@@ -15,6 +14,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
enum class SearchCategory(val label: String) {
|
enum class SearchCategory(val label: String) {
|
||||||
ALL("全部"),
|
ALL("全部"),
|
||||||
@@ -24,6 +24,11 @@ enum class SearchCategory(val label: String) {
|
|||||||
CONTAINER("容器")
|
CONTAINER("容器")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class OpsSysNavInfo(
|
||||||
|
val module: String,
|
||||||
|
val id: Int
|
||||||
|
)
|
||||||
|
|
||||||
data class SearchUiState(
|
data class SearchUiState(
|
||||||
val query: String = "",
|
val query: String = "",
|
||||||
val category: SearchCategory = SearchCategory.ALL,
|
val category: SearchCategory = SearchCategory.ALL,
|
||||||
@@ -32,7 +37,8 @@ data class SearchUiState(
|
|||||||
val items: List<Item> = emptyList(),
|
val items: List<Item> = emptyList(),
|
||||||
val containers: List<Container> = emptyList(),
|
val containers: List<Container> = emptyList(),
|
||||||
val isSearching: Boolean = false,
|
val isSearching: Boolean = false,
|
||||||
val errorMessage: String? = null
|
val errorMessage: String? = null,
|
||||||
|
val opsSysNavigation: OpsSysNavInfo? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
class SearchViewModel(
|
class SearchViewModel(
|
||||||
@@ -61,7 +67,19 @@ class SearchViewModel(
|
|||||||
val query = _uiState.value.query.trim()
|
val query = _uiState.value.query.trim()
|
||||||
if (query.isEmpty()) return
|
if (query.isEmpty()) return
|
||||||
|
|
||||||
// Parse prefix shortcuts
|
val jsonObj = try { JSONObject(query) } catch (e: Exception) { null }
|
||||||
|
if (jsonObj != null) {
|
||||||
|
_uiState.value = _uiState.value.copy(query = "")
|
||||||
|
if (jsonObj.optString("type") == "ops_sys") {
|
||||||
|
val module = jsonObj.optString("module")
|
||||||
|
val id = jsonObj.optInt("id")
|
||||||
|
if (module.isNotBlank() && id > 0) {
|
||||||
|
_uiState.value = _uiState.value.copy(opsSysNavigation = OpsSysNavInfo(module, id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val (actualCategory, actualQuery) = parsePrefix(query)
|
val (actualCategory, actualQuery) = parsePrefix(query)
|
||||||
_uiState.value = _uiState.value.copy(category = actualCategory)
|
_uiState.value = _uiState.value.copy(category = actualCategory)
|
||||||
|
|
||||||
@@ -125,6 +143,10 @@ class SearchViewModel(
|
|||||||
_uiState.value = SearchUiState(query = _uiState.value.query, category = _uiState.value.category)
|
_uiState.value = SearchUiState(query = _uiState.value.query, category = _uiState.value.category)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearOpsSysNavigation() {
|
||||||
|
_uiState.value = _uiState.value.copy(opsSysNavigation = null)
|
||||||
|
}
|
||||||
|
|
||||||
private fun parsePrefix(query: String): Pair<SearchCategory, String> {
|
private fun parsePrefix(query: String): Pair<SearchCategory, String> {
|
||||||
val prefixes = mapOf(
|
val prefixes = mapOf(
|
||||||
"po:" to SearchCategory.ORDER,
|
"po:" to SearchCategory.ORDER,
|
||||||
|
|||||||
Reference in New Issue
Block a user