Explorar el Código

1. 新增上传表单,动态配置,按需跳转系统选择图片或者文件

bjb hace 3 meses
padre
commit
de96c42cd5

+ 2 - 2
.idea/deploymentTargetSelector.xml

@@ -4,10 +4,10 @@
     <selectionStates>
       <SelectionState runConfigName="app">
         <option name="selectionMode" value="DROPDOWN" />
-        <DropdownSelection timestamp="2026-01-28T05:55:40.229413700Z">
+        <DropdownSelection timestamp="2026-02-05T06:40:21.375355300Z">
           <Target type="DEFAULT_BOOT">
             <handle>
-              <DeviceId pluginId="PhysicalDevice" identifier="serial=d8d12db95670c08" />
+              <DeviceId pluginId="PhysicalDevice" identifier="serial=14381b6d" />
             </handle>
           </Target>
         </DropdownSelection>

+ 137 - 1
app/src/main/java/com/iscs/bozzys/ui/pages/compose/FormCompose.kt

@@ -1,8 +1,14 @@
 package com.iscs.bozzys.ui.pages.compose
 
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Build
 import android.util.Log
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
 import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ExperimentalLayoutApi
@@ -64,8 +70,11 @@ import com.loper7.date_time_picker.dialog.CardDatePickerDialog
 import kotlinx.serialization.json.Json
 
 
+/**
+ * 表单容器,可以根据后台配置的表单格式进行解析各种表单
+ */
 @Composable
-fun FormContainer(forms: List<FormField>, onValueChange: (FormField) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true) {
+fun FormBox(forms: List<FormField>, onValueChange: (FormField) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true) {
     Column(modifier = modifier) {
         forms.forEach { form ->
             key(form.id) {
@@ -168,6 +177,18 @@ fun FormContainer(forms: List<FormField>, onValueChange: (FormField) -> Unit, mo
                         required = form.required,
                         enable = form.enabled && enabled
                     )
+                    // 图片上传
+                    "image" -> FormImage(
+                        form.label,
+                        form.value,
+                        form.options,
+                        {
+                            form.value = it
+                            onValueChange(form)
+                        },
+                        required = form.required,
+                        enable = form.enabled && enabled
+                    )
                 }
             }
         }
@@ -606,6 +627,121 @@ fun FormCheckbox(
     }
 }
 
+/**
+ * 照片上传组件
+ *
+ * @param label             标题
+ * @param value             当前选中
+ * @param options           可选列表
+ * @param onSelectChange    当前选中
+ */
+@OptIn(ExperimentalLayoutApi::class)
+@Composable
+fun FormImage(
+    label: String,
+    value: List<String>,
+    options: List<FormOption>,
+    onSelectChange: (List<String>) -> Unit,
+    required: Boolean = false,
+    enable: Boolean = true,
+) {
+    val ctx = LocalContext.current
+    // 启动页面
+    val activityLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri ->
+
+    }
+    // 权限请求
+    val permissionLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) {
+        if (it) {
+            activityLauncher.launch("image/*")
+        }
+    }
+    val values = remember { mutableStateListOf<String>() }
+    LaunchedEffect(Unit) {
+        values.clear()
+        values.addAll(value)
+    }
+    Column(
+        Modifier
+            .fillMaxWidth()
+            .heightIn(max = 160.dp)
+    ) {
+        Row(
+            Modifier
+                .fillMaxWidth()
+                .height(40.dp), verticalAlignment = Alignment.CenterVertically
+        ) {
+            Text(
+                label,
+                fontSize = 14.sp,
+                fontWeight = FontWeight.Bold,
+                color = Text.copy(alpha = if (enable) 1f else 0.6f)
+            )
+            if (required) Text(
+                "*",
+                fontSize = 14.sp,
+                color = Color(0xFFFF4D4F).copy(alpha = if (enable) 1f else 0.6f),
+                modifier = Modifier.padding(start = 3.dp)
+            )
+        }
+        FlowRow(
+            Modifier
+                .fillMaxWidth()
+                .heightIn(max = 120.dp)
+        ) {
+            if (options.isEmpty()) {
+                // 没有默认数据时,显示点击上传
+                Column(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .height(120.dp)
+                        .border(1.dp, shape = RoundedCornerShape(6.dp), color = Color(0xFFE5E6EB))
+                        .clip(RoundedCornerShape(6.dp))
+                        .clickable {
+                            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+                                if (ctx.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+                                    permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
+                                } else {
+                                    activityLauncher.launch("image/*")
+                                }
+                            } else {
+                                activityLauncher.launch("image/*")
+                            }
+                        },
+                    verticalArrangement = Arrangement.Center,
+                    horizontalAlignment = Alignment.CenterHorizontally
+                ) {
+                    Icon(
+                        painter = painterResource(R.drawable.upload),
+                        contentDescription = null,
+                        modifier = Modifier
+                            .padding(bottom = 5.dp)
+                            .size(50.dp),
+                        tint = Text.copy(alpha = 0.3f)
+                    )
+                    Text("点击上传", fontSize = 14.sp, color = Text.copy(alpha = 0.3f))
+                }
+            }
+            options.forEach { item ->
+                Row(
+                    modifier = Modifier
+                        .clip(RoundedCornerShape(12.dp))
+                        .clickable(onClick = {
+                            if (values.contains(item.value))
+                                values.remove(item.value)
+                            else values.add(item.value)
+                            onSelectChange(values)
+                        }, enabled = enable)
+                        .padding(horizontal = 10.dp, vertical = 5.dp),
+                    verticalAlignment = Alignment.CenterVertically
+                ) {
+
+                }
+            }
+        }
+    }
+}
+
 /**
  * 表单日期时间选择组件
  *

+ 2 - 2
app/src/main/java/com/iscs/bozzys/ui/pages/create/job/PageCreateJob.kt

@@ -37,7 +37,7 @@ import com.iscs.bozzys.event.RefreshEventBus
 import com.iscs.bozzys.ui.common.PageBase
 import com.iscs.bozzys.ui.common.Title
 import com.iscs.bozzys.ui.pages.compose.CardBox
-import com.iscs.bozzys.ui.pages.compose.FormContainer
+import com.iscs.bozzys.ui.pages.compose.FormBox
 import com.iscs.bozzys.ui.pages.edit.step.openPageEditStep
 import com.iscs.bozzys.ui.pages.vm.VMCreateJob
 
@@ -72,7 +72,7 @@ class PageCreateJob : PageBase() {
             ) {
                 // 表单内容
                 if (state.forms.isNotEmpty()) CardBox(Modifier.padding(horizontal = 16.dp, vertical = 16.dp)) {
-                    FormContainer(
+                    FormBox(
                         state.forms, {}, modifier = Modifier
                             .padding(horizontal = 10.dp)
                             .padding(top = 5.dp, bottom = 16.dp)

+ 2 - 2
app/src/main/java/com/iscs/bozzys/ui/pages/detail/task/PageDetailTask.kt

@@ -42,7 +42,7 @@ import com.iscs.bozzys.api.Task
 import com.iscs.bozzys.ui.common.PageBase
 import com.iscs.bozzys.ui.common.Title
 import com.iscs.bozzys.ui.pages.compose.CardBox
-import com.iscs.bozzys.ui.pages.compose.FormContainer
+import com.iscs.bozzys.ui.pages.compose.FormBox
 import com.iscs.bozzys.ui.pages.vm.VMDetailTask
 import com.iscs.bozzys.ui.theme.Text
 
@@ -256,7 +256,7 @@ class PageDetailTask : PageBase() {
                     )
                     Text("录入信息", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Text)
                 }
-                FormContainer(state.forms, {}, enabled = state.node.approvalStatus != "approved")
+                FormBox(state.forms, {}, enabled = state.node.approvalStatus != "approved")
             }
         }
     }

+ 2 - 2
app/src/main/java/com/iscs/bozzys/ui/pages/edit/step/PageEditStep.kt

@@ -61,7 +61,7 @@ import com.iscs.bozzys.ui.common.PageBase
 import com.iscs.bozzys.ui.dialog.TipsDialog
 import com.iscs.bozzys.ui.common.Title
 import com.iscs.bozzys.ui.pages.compose.CardBox
-import com.iscs.bozzys.ui.pages.compose.FormContainer
+import com.iscs.bozzys.ui.pages.compose.FormBox
 import com.iscs.bozzys.ui.pages.create.job.openPagePushJob
 import com.iscs.bozzys.ui.pages.edit.step.compose.NodeItem
 import com.iscs.bozzys.ui.pages.edit.step.compose.ZoomPanContainer
@@ -147,7 +147,7 @@ class PageEditStep : PageBase() {
                                         .imePadding()
                                         .padding(vertical = 5.dp)
                                 ) {
-                                    item { FormContainer(state.nodeForms, { vm.onNodeFormChanged(it) }, enabled = state.enableEdit) }
+                                    item { FormBox(state.nodeForms, { vm.onNodeFormChanged(it) }, enabled = state.enableEdit) }
                                 }
                             }
                             if (imeBottom <= 0) {

+ 3 - 2
app/src/main/java/com/iscs/bozzys/ui/pages/vm/VMCreateJob.kt

@@ -7,6 +7,7 @@ import com.iscs.bozzys.api.FormField
 import com.iscs.bozzys.api.FormOption
 import com.iscs.bozzys.ui.common.VMBase
 import com.iscs.bozzys.ui.pages.compose.getFormListByJsonList
+import com.iscs.bozzys.utils.LogUtil
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -21,8 +22,7 @@ class VMCreateJob : VMBase() {
     val state = _state.asStateFlow()
 
     // 表单基准数据
-    val json =
-        "[{\"id\":\"job_name\",\"label\":\"作业名称\",\"value\":[\"\"],\"type\":\"input\",\"placeholder\":[\"请输入作业名称\"],\"required\":true},{\"id\":\"job_sop\",\"label\":\"流程模板\",\"value\":[\"\"],\"type\":\"select\",\"placeholder\":[\"\"],\"required\":true,\"options\":[]},{\"id\":\"job_type\",\"label\":\"作业分类\",\"value\":[\"\"],\"type\":\"select\",\"placeholder\":[\"\"],\"options\":[],\"required\":true},{\"id\":\"job_content\",\"label\":\"作业内容\",\"value\":[\"\"],\"type\":\"textarea\",\"placeholder\":[\"\"],\"required\":true},{\"id\":\"job_urgency_level\",\"label\":\"紧急程度\",\"value\":[\"0\"],\"type\":\"radio\",\"placeholder\":[\"\"],\"required\":false,\"options\":[{\"label\":\"普通\",\"value\":\"0\"},{\"label\":\"紧急\",\"value\":\"1\"},{\"label\":\"非常紧急\",\"value\":\"2\"}]}]"
+    val json = "[{\"id\":\"job_name\",\"label\":\"作业名称\",\"value\":[\"\"],\"type\":\"input\",\"placeholder\":[\"请输入作业名称\"],\"required\":true},{\"id\":\"job_sop\",\"label\":\"流程模板\",\"value\":[\"\"],\"type\":\"select\",\"placeholder\":[\"\"],\"required\":true,\"options\":[]},{\"id\":\"job_type\",\"label\":\"作业分类\",\"value\":[\"\"],\"type\":\"select\",\"placeholder\":[\"\"],\"options\":[],\"required\":true},{\"id\":\"job_content\",\"label\":\"作业内容\",\"value\":[\"\"],\"type\":\"textarea\",\"placeholder\":[\"\"],\"required\":true},{\"id\":\"job_urgency_level\",\"label\":\"紧急程度\",\"value\":[\"0\"],\"type\":\"radio\",\"placeholder\":[\"\"],\"required\":false,\"options\":[{\"label\":\"普通\",\"value\":\"0\"},{\"label\":\"紧急\",\"value\":\"1\"},{\"label\":\"非常紧急\",\"value\":\"2\"}]}]"
 
     // 当前作业id
     private var id = 0
@@ -33,6 +33,7 @@ class VMCreateJob : VMBase() {
      */
     fun init(id: Int) {
         this.id = id
+        LogUtil.d("JobCreate", "创建时需要的表单 -> $json")
         viewModelScope.launch {
             val job = if (id > 0) ApiRequest.getJobById(id).getOrElse { it.getResponse() }.data else null
             val forms = json.getFormListByJsonList(job)

+ 15 - 0
app/src/main/res/drawable/upload.xml

@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40.3dp"
+    android:height="32dp"
+    android:viewportWidth="1291"
+    android:viewportHeight="1024">
+  <path
+      android:pathData="M779.7,669.4a31.5,31.5 0,0 1,-22.4 -9.3L645.9,548.7l-111.4,111.4a31.5,31.5 0,0 1,-44.6 -44.6l133.7,-133.6a31.5,31.5 0,0 1,44.4 0l133.7,133.6a31.5,31.5 0,0 1,-22.2 53.9z"
+      android:fillColor="#1296db"/>
+  <path
+      android:pathData="M961,1024H787.7a31.5,31.5 0,0 1,0 -63h166.4a29.3,29.3 0,0 1,5.4 0,283.6 283.6,0 0,0 55,-557.5 31.5,31.5 0,0 1,-23.6 -27.4,346.6 346.6,0 0,0 -689.7,0 31.5,31.5 0,0 1,-23.8 27.4A283.6,283.6 0,0 0,332.4 961a31.5,31.5 0,0 1,5.5 0H645.9a31.5,31.5 0,0 1,0 63H330.8a33.4,33.4 0,0 1,-9 -1.3,346.6 346.6,0 0,1 -80.8,-675.1 409.6,409.6 0,0 1,809.7 0,346.6 346.6,0 0,1 -81.1,675.2A31.5,31.5 0,0 1,961 1024z"
+      android:fillColor="#1296db"/>
+  <path
+      android:pathData="M645.9,1008.2a31.5,31.5 0,0 1,-31.5 -31.5V551.4a31.5,31.5 0,0 1,63 0v425.4a31.5,31.5 0,0 1,-31.5 31.5z"
+      android:fillColor="#1296db"/>
+</vector>