Просмотр исходного кода

2025-11-17 修改

修改:
1. 团组管理模块 - 部门任务 bug修改
zhaiy месяцев назад: 5
Родитель
Сommit
36b23fdcda
18 измененных файлов с 863 добавлено и 69 удалено
  1. 1 1
      app/src/main/java/com/pan_american/android/base/BaseActivity.kt
  2. 2 2
      app/src/main/java/com/pan_american/android/data/model/group_management/department_process/adapter/DepartmentProcessDetailTaskListAdapter.kt
  3. 8 1
      app/src/main/java/com/pan_american/android/data/model/group_management/department_process/adapter/DepartmentProcessListAdapter.kt
  4. 11 0
      app/src/main/java/com/pan_american/android/data/model/group_management/department_process/entity/DepartmentDetailBaseData.kt
  5. 6 10
      app/src/main/java/com/pan_american/android/data/model/group_management/department_process/entity/DepartmentProcessDetail.kt
  6. 1 1
      app/src/main/java/com/pan_american/android/data/model/group_management/department_process/network/GetDepartmentProcessListRequest.kt
  7. 3 0
      app/src/main/java/com/pan_american/android/data/model/group_management/department_process/network/InitDepartmentBaseDataRequest.kt
  8. 6 0
      app/src/main/java/com/pan_american/android/data/model/group_management/department_process/network/InitDepartmentBaseDataResponse.kt
  9. 14 0
      app/src/main/java/com/pan_american/android/data/network/APIService.kt
  10. 15 1
      app/src/main/java/com/pan_american/android/ui/group_management/department_process/DepartmentProcessActivity.kt
  11. 177 26
      app/src/main/java/com/pan_american/android/ui/group_management/department_process/DepartmentProcessDetailActivity.kt
  12. 13 0
      app/src/main/java/com/pan_american/android/ui/group_management/department_process/DepartmentProcessMissionListFragment.kt
  13. 393 3
      app/src/main/java/com/pan_american/android/ui/group_management/department_process/MissionProcessHistoryFragment.kt
  14. 0 4
      app/src/main/java/com/pan_american/android/ui/group_management/department_process/MissionProcessListFragment.kt
  15. 11 20
      app/src/main/java/com/pan_american/android/ui/group_visa/visa_process/VisaProcessDetailActivity.kt
  16. 9 0
      app/src/main/res/layout/fragment_department_process_mission_list.xml
  17. 184 0
      app/src/main/res/layout/popup_add_mission_history.xml
  18. 9 0
      app/src/main/res/values/strings.xml

+ 1 - 1
app/src/main/java/com/pan_american/android/base/BaseActivity.kt

@@ -165,7 +165,7 @@ abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
      */
     fun showSelector(block: () -> Unit) {
         selectorPopView = View.inflate(this, R.layout.popup_smart_refresh_selector, null)
-        val popupWindow = PopupWindow(
+        popupWindow = PopupWindow(
             selectorPopView,
             ViewGroup.LayoutParams.MATCH_PARENT,
             ViewGroup.LayoutParams.WRAP_CONTENT

+ 2 - 2
app/src/main/java/com/pan_american/android/data/model/group_management/department_process/adapter/DepartmentProcessDetailTaskListAdapter.kt

@@ -17,7 +17,7 @@ class DepartmentProcessDetailTaskListAdapter(
     val dataList: ArrayList<TaskDetail>,
     val priorityList: ArrayList<Selector>,
     val directorList: ArrayList<Selector>,
-    val assignedUserId: Int
+    val editable: Boolean
 ) :
     RecyclerView.Adapter<DepartmentProcessDetailTaskListAdapter.ViewHolder>() {
 
@@ -42,7 +42,7 @@ class DepartmentProcessDetailTaskListAdapter(
             .inflate(R.layout.item_mission_detail_list, parent, false)
         val viewHolder = ViewHolder(view)
 
-        if (assignedUserId != OASystem.userInfo.userId) {
+        if (!editable) {
             viewHolder.deleteButton.visibility = View.GONE
 
             viewHolder.buttonGroup.visibility = View.GONE

+ 8 - 1
app/src/main/java/com/pan_american/android/data/model/group_management/department_process/adapter/DepartmentProcessListAdapter.kt

@@ -31,7 +31,8 @@ class DepartmentProcessListAdapter(val dataList: ArrayList<DepartmentProcessItem
         parent: ViewGroup,
         viewType: Int
     ): ViewHolder {
-        val view = LayoutInflater.from(OASystem.context).inflate(R.layout.item_department_process_list, parent, false)
+        val view = LayoutInflater.from(OASystem.context)
+            .inflate(R.layout.item_department_process_list, parent, false)
         val viewHolder = ViewHolder(view)
 
         if (OASystem.authorization(OASystem.DEPARTMENT_PROCESS, OASystem.DELETE)) {
@@ -67,6 +68,12 @@ class DepartmentProcessListAdapter(val dataList: ArrayList<DepartmentProcessItem
             holder.director.text = assignee
             holder.foreignAffairs.text = externalOption
         }
+
+        if (!dataList[position].isReview) {
+            holder.deleteButton.visibility = View.GONE
+        } else {
+            holder.deleteButton.visibility = View.VISIBLE
+        }
     }
 
     override fun getItemCount() = dataList.size

+ 11 - 0
app/src/main/java/com/pan_american/android/data/model/group_management/department_process/entity/DepartmentDetailBaseData.kt

@@ -0,0 +1,11 @@
+package com.pan_american.android.data.model.group_management.department_process.entity
+
+import com.pan_american.android.data.model.common.entity.Selector
+
+open class DepartmentDetailBaseData() {
+    val groupList =  ArrayList<Selector>()
+    val taskLv =  ArrayList<Selector>()
+    val foreignLv = ArrayList<Selector>()
+    val users =  ArrayList<Selector>()
+    val defaultTask = ArrayList<TaskDetail>()
+}

+ 6 - 10
app/src/main/java/com/pan_american/android/data/model/group_management/department_process/entity/DepartmentProcessDetail.kt

@@ -1,17 +1,12 @@
 package com.pan_american.android.data.model.group_management.department_process.entity
 
 import com.pan_american.android.OASystem
-import com.pan_american.android.data.model.common.entity.Selector
 
-class DepartmentProcessDetail(
-    val groupList: ArrayList<Selector>,
-    val taskLv: ArrayList<Selector>,
-    val foreignLv: ArrayList<Selector>,
-    val users: ArrayList<Selector>,
-    val workOrder: WorkOrder,
-    val mainTask: ArrayList<TaskDetail>,
-    val extraTask: ArrayList<TaskDetail>
-)
+class DepartmentProcessDetail() : DepartmentDetailBaseData() {
+    val workOrder = WorkOrder()
+    val mainTask = ArrayList<TaskDetail>()
+    val extraTask = ArrayList<TaskDetail>()
+}
 
 open class WorkOrder {
     var id = 0
@@ -22,6 +17,7 @@ open class WorkOrder {
     var groupId = 0
     var startTime = ""
     var action = 1
+    val typeId = 1453
     val createUserId = OASystem.userInfo.userId
     val tasks = ArrayList<TaskDetail>()
 }

+ 1 - 1
app/src/main/java/com/pan_american/android/data/model/group_management/department_process/network/GetDepartmentProcessListRequest.kt

@@ -6,7 +6,7 @@ import com.pan_american.android.base.BaseRequest
 
 class GetDepartmentProcessListRequest(
     @SerializedName("search_Name") var searchName: String,
-    val isLv: Int
+    var isLv: Int
 ) : BaseRequest() {
     val userId = OASystem.userInfo.userId
     val typeId = 1453

+ 3 - 0
app/src/main/java/com/pan_american/android/data/model/group_management/department_process/network/InitDepartmentBaseDataRequest.kt

@@ -0,0 +1,3 @@
+package com.pan_american.android.data.model.group_management.department_process.network
+
+class InitDepartmentBaseDataRequest(val typeId: Int = 1453)

+ 6 - 0
app/src/main/java/com/pan_american/android/data/model/group_management/department_process/network/InitDepartmentBaseDataResponse.kt

@@ -0,0 +1,6 @@
+package com.pan_american.android.data.model.group_management.department_process.network
+
+import com.pan_american.android.base.BaseResponse
+import com.pan_american.android.data.model.group_management.department_process.entity.DepartmentDetailBaseData
+
+class InitDepartmentBaseDataResponse(val data: DepartmentDetailBaseData): BaseResponse()

+ 14 - 0
app/src/main/java/com/pan_american/android/data/network/APIService.kt

@@ -162,6 +162,8 @@ import com.pan_american.android.data.model.group_management.department_process.n
 import com.pan_american.android.data.model.group_management.department_process.network.GetDepartmentProcessListResponse
 import com.pan_american.android.data.model.group_management.department_process.network.GetMissionReceiptRequest
 import com.pan_american.android.data.model.group_management.department_process.network.GetMissionReceiptResponse
+import com.pan_american.android.data.model.group_management.department_process.network.InitDepartmentBaseDataRequest
+import com.pan_american.android.data.model.group_management.department_process.network.InitDepartmentBaseDataResponse
 import com.pan_american.android.data.model.group_management.department_process.network.UpdateMissionStatusWithNoReceiptRequest
 import com.pan_american.android.data.model.group_management.department_process.network.VerifyMissionRequest
 import com.pan_american.android.data.model.group_management.entry_and_exit_fee_detail.network.DeleteEntryAndExitPaymentItemRequest
@@ -1811,6 +1813,12 @@ interface APIService {
     @POST("/api/Task/GetTaskListByTaskId")
     fun getDepartmentProcessItemById(@Body getDepartmentProcessItemRequest: GetDepartmentProcessItemRequest): Call<GetDepartmentProcessItemResponse>
 
+    /**
+     * 部门任务,新增任务获取基础数据源和默认任务
+     */
+    @POST("/api/Task/TaskInit")
+    fun initDepartmentBaseData(@Body initDepartmentBaseDataRequest: InitDepartmentBaseDataRequest): Call<InitDepartmentBaseDataResponse>
+
     /**
      * 部门任务,根据任务id获取任务详情
      */
@@ -1840,4 +1848,10 @@ interface APIService {
      */
     @POST("/api/Task/AuditWorkTaskReceipt")
     fun updateMissionStatusVerify(@Body verifyMissionRequest: VerifyMissionRequest): Call<BaseResponse>
+
+    /**
+     * 部门任务,任务回执提交
+     */
+    @POST("/api/Task/SubmitWorkTaskReceipt")
+    fun uploadMissionReceipt(@Body requestBody: RequestBody): Call<BaseResponse>
 }

+ 15 - 1
app/src/main/java/com/pan_american/android/ui/group_management/department_process/DepartmentProcessActivity.kt

@@ -1,6 +1,9 @@
 package com.pan_american.android.ui.group_management.department_process
 
+import android.content.Intent
 import android.os.Bundle
+import android.view.View
+import com.pan_american.android.OASystem
 import com.pan_american.android.R
 import com.pan_american.android.base.BaseActivity
 import com.pan_american.android.databinding.ActivityDepartmentProcessBinding
@@ -23,6 +26,16 @@ class DepartmentProcessActivity : BaseActivity<ActivityDepartmentProcessBinding>
         titleBinding = LayoutTitleBinding.bind(binding.root).apply {
             titleText.text = resources.getString(R.string.department_process)
 
+            rightTextField.visibility = View.VISIBLE
+
+            rightTextField.text = resources.getString(R.string.add)
+
+            rightTextField.setOnClickListener {
+                val intent = Intent(OASystem.context, DepartmentProcessDetailActivity::class.java)
+
+                startActivity(intent)
+            }
+
             backButton.setOnClickListener {
                 back()
             }
@@ -33,6 +46,7 @@ class DepartmentProcessActivity : BaseActivity<ActivityDepartmentProcessBinding>
         val departmentProcessMissionListFragment = DepartmentProcessMissionListFragment()
 
         supportFragmentManager.beginTransaction().addToBackStack(null)
-            .add(binding.departmentProcessContainer.id, departmentProcessMissionListFragment).commit()
+            .add(binding.departmentProcessContainer.id, departmentProcessMissionListFragment)
+            .commit()
     }
 }

+ 177 - 26
app/src/main/java/com/pan_american/android/ui/group_management/department_process/DepartmentProcessDetailActivity.kt

@@ -1,6 +1,7 @@
 package com.pan_american.android.ui.group_management.department_process
 
 import android.os.Bundle
+import android.util.Log
 import android.view.Gravity
 import android.view.View
 import android.view.ViewGroup
@@ -14,6 +15,7 @@ import androidx.core.content.res.ResourcesCompat
 import androidx.core.widget.addTextChangedListener
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
+import com.google.gson.Gson
 import com.pan_american.android.OASystem
 import com.pan_american.android.R
 import com.pan_american.android.base.BaseActivity
@@ -27,6 +29,8 @@ import com.pan_american.android.data.model.group_management.department_process.e
 import com.pan_american.android.data.model.group_management.department_process.entity.TaskDetail
 import com.pan_american.android.data.model.group_management.department_process.network.GetDepartmentProcessDetailRequest
 import com.pan_american.android.data.model.group_management.department_process.network.GetDepartmentProcessDetailResponse
+import com.pan_american.android.data.model.group_management.department_process.network.InitDepartmentBaseDataRequest
+import com.pan_american.android.data.model.group_management.department_process.network.InitDepartmentBaseDataResponse
 import com.pan_american.android.databinding.ActivityDepartmentProcessDetailBinding
 import com.pan_american.android.databinding.LayoutTitleBinding
 import com.pan_american.android.util.ScrollEditText
@@ -40,6 +44,8 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
 
     private var fromList = false
 
+    private var editable = false
+
     private var taskId = 0
 
     private val groupList = ArrayList<Selector>()
@@ -50,7 +56,7 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
 
     private val userList = ArrayList<Selector>()
 
-    private lateinit var departmentProcessDetail: DepartmentProcessDetail
+    private var departmentProcessDetail = DepartmentProcessDetail()
 
     override fun getViewBinding() = ActivityDepartmentProcessDetailBinding.inflate(layoutInflater)
 
@@ -62,6 +68,9 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
 
             if (fromList) {
                 taskId = getIntExtra("task_id", 0)
+                editable = getBooleanExtra("can_process", false)
+            } else {
+                editable = true
             }
         }
 
@@ -76,12 +85,43 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
         titleBinding = LayoutTitleBinding.bind(binding.root).apply {
             titleText.text = resources.getString(R.string.visa_process)
 
-            rightTextField.visibility = View.VISIBLE
+            if (editable) {
+                rightTextField.visibility = View.VISIBLE
+
+                rightTextField.setText(R.string.save)
+
+                rightTextField.setOnClickListener {
+                    if (binding.processName.text.isNullOrBlank()) {
+                        showMessage(resources.getString(R.string.process_name_input_hint))
+                        return@setOnClickListener
+                    } else {
+                        departmentProcessDetail.workOrder.name = binding.processName.text.toString()
+                    }
 
-            rightTextField.setText(R.string.save)
+                    if (binding.startTime.text.isNullOrBlank()) {
+                        showMessage(resources.getString(R.string.start_time_select_hint))
+                        return@setOnClickListener
+                    } else {
+                        departmentProcessDetail.workOrder.startTime =
+                            binding.startTime.text.toString()
+                    }
+
+                    if (departmentProcessDetail.workOrder.groupId == 0) {
+                        showMessage(resources.getString(R.string.association_group_selected_hint))
+                        return@setOnClickListener
+                    }
+
+                    if (departmentProcessDetail.workOrder.assignedUserId == 0) {
+                        showMessage(resources.getString(R.string.director_select_hint))
+                        return@setOnClickListener
+                    }
 
-            rightTextField.setOnClickListener {
-                updateDepartmentProcess()
+                    if (departmentProcessDetail.workOrder.foreignOptionId == 0) {
+                        showMessage(resources.getString(R.string.foreign_affairs_select_hint))
+                        return@setOnClickListener
+                    }
+                    updateDepartmentProcess()
+                }
             }
 
             backButton.setOnClickListener {
@@ -96,6 +136,18 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
         } else {
             getAddBaseData()
         }
+
+        if (!editable) {
+
+            binding.processName.isEnabled = false
+            binding.startTime.isEnabled = false
+            binding.associationGroup.isEnabled = false
+            binding.director.isEnabled = false
+            binding.foreignAffairsOption.isEnabled = false
+
+            binding.addMainMission.visibility = View.GONE
+            binding.addExtraMission.visibility = View.GONE
+        }
     }
 
     override fun initEvents() {
@@ -127,7 +179,9 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
             showDateAndTimePicker(
                 resources.getString(R.string.start_time_select_hint),
                 binding.startTime
-            )
+            ) {
+                departmentProcessDetail.workOrder.startTime = binding.startTime.text.toString()
+            }
         }
 
         binding.associationGroup.setOnClickListener {
@@ -176,7 +230,7 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
                                 )
                             }
 
-                            text = data.country
+                            text = data.teamName
                         }
                     }
                 }.create()
@@ -189,6 +243,9 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
 
                             binding.associationGroup.text = selectDataSource[position].teamName
 
+                            departmentProcessDetail.workOrder.groupId =
+                                selectDataSource[position].id
+
                             popupWindow.dismiss()
                         }
                     }
@@ -257,7 +314,7 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
                                 )
                             }
 
-                            text = data.country
+                            text = data.cnName
                         }
                     }
                 }.create()
@@ -270,6 +327,9 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
 
                             binding.director.text = selectDataSource[position].cnName
 
+                            departmentProcessDetail.workOrder.assignedUserId =
+                                selectDataSource[position].id
+
                             popupWindow.dismiss()
                         }
                     }
@@ -328,7 +388,7 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
                     object : ListAdapter.OnRecyclerViewItemClick {
                         override fun onItemClick(position: Int) {
 
-                            OASystem.weight[position].apply {
+                            foreignSelectionList[position].apply {
                                 binding.foreignAffairsOption.text = name
                                 departmentProcessDetail.workOrder.foreignOptionId = id
                             }
@@ -349,7 +409,58 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
     }
 
     private fun getAddBaseData() {
+        OASystem.apiService.initDepartmentBaseData(InitDepartmentBaseDataRequest())
+            .enqueue(object : Callback<InitDepartmentBaseDataResponse> {
+                override fun onResponse(
+                    call: Call<InitDepartmentBaseDataResponse?>,
+                    response: Response<InitDepartmentBaseDataResponse?>
+                ) {
+                    val dataResponse = response.body()
+
+                    if (dataResponse != null) {
 
+                        if (dataResponse.code == 200) {
+
+                            for (item in dataResponse.data.groupList) {
+                                groupList.add(item)
+                            }
+
+                            for (item in dataResponse.data.taskLv) {
+                                taskLevelList.add(item)
+                            }
+
+                            for (item in dataResponse.data.foreignLv) {
+                                foreignSelectionList.add(item)
+                            }
+
+                            for (item in dataResponse.data.users) {
+                                userList.add(item)
+                            }
+
+                            for (item in dataResponse.data.defaultTask) {
+                                departmentProcessDetail.mainTask.add(item)
+                            }
+
+                            initDetailView()
+
+                        } else {
+                            showErrorInfo(R.string.base_resource_data_get_failed)
+                        }
+
+                    } else {
+                        showErrorInfo(R.string.interface_request_error)
+                        finish()
+                    }
+                }
+
+                override fun onFailure(
+                    call: Call<InitDepartmentBaseDataResponse?>,
+                    t: Throwable
+                ) {
+                    showErrorInfo(R.string.network_error)
+                    finish()
+                }
+            })
     }
 
     private fun getProcessDetail(taskId: Int) {
@@ -433,13 +544,13 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
             departmentProcessDetail.mainTask,
             priorityList = taskLevelList,
             directorList = userList,
-            assignedUserId = departmentProcessDetail.workOrder.assignedUserId
+            editable = editable
         )
 
         val extraAdapter = CardAdapter.Builder<TaskDetail>().apply {
             setData(departmentProcessDetail.extraTask)
             setLayoutId(R.layout.item_mission_detail_list)
-            setCanDelete(departmentProcessDetail.workOrder.assignedUserId == OASystem.userInfo.userId)
+            setCanDelete(editable)
             addBindView { itemView, data ->
                 itemView.findViewById<TextView>(R.id.mission_name).text = data.name
                 itemView.findViewById<TextView>(R.id.priority).apply {
@@ -520,7 +631,9 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
 
         extraAdapter.onRecyclerViewItemClick = object : CardAdapter.OnRecyclerViewItemClick {
             override fun onItemClick(position: Int) {
-                updateExtraTask(position)
+                if (editable) {
+                    updateExtraTask(position)
+                }
             }
 
             override fun onItemDelete(position: Int) {
@@ -755,7 +868,7 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
 
                 val searchSelectorView =
                     View.inflate(OASystem.context, R.layout.popup_list_selector_with_search, null)
-                
+
                 // 创建嵌套的 popupWindow,使用局部变量
                 val nestedPopupWindow = PopupWindow(
                     searchSelectorView,
@@ -773,7 +886,7 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
                     animationStyle = R.style.AnimationBottomPopup
                     isOutsideTouchable = true
                     isFocusable = true
-                    
+
                     setOnDismissListener {
                         // 嵌套窗口关闭时,恢复主窗口的外部点击关闭功能
                         mainPopupWindow.isOutsideTouchable = wasMainWindowOutsideTouchable
@@ -851,24 +964,26 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
                 // 保存主 popupWindow 的引用和状态,避免被嵌套窗口覆盖
                 val savedMainPopupWindow = mainPopupWindow
                 val wasMainWindowOutsideTouchable = mainPopupWindow.isOutsideTouchable
-                
+
                 // 临时禁用主窗口的外部点击关闭功能,避免嵌套窗口关闭时主窗口也被关闭
                 mainPopupWindow.isOutsideTouchable = false
-                
+
                 showDateAndTimePicker(
                     resources.getString(R.string.start_time_select_hint),
                     startTime
-                )
-                
+                ) {
+                    taskDetail.startTime = startTime.text.toString() + ":00"
+                }
+
                 // showDateAndTimePicker 内部会调用 showPopupWindow,覆盖 popupWindow
                 // showPopupWindow 在 block 执行前就设置了 dismiss 监听器,会将窗口 alpha 恢复为 1f
-                // 我们需要立即重新设置 dismiss 监听器,确保遮罩层(主窗口的 alpha)不被恢复
+                // 立即重新设置 dismiss 监听器,确保遮罩层(主窗口的 alpha)不被恢复
                 val nestedDatePickerWindow = popupWindow
                 // 立即重新设置 dismiss 监听器,覆盖 showPopupWindow 设置的监听器
                 nestedDatePickerWindow.setOnDismissListener {
                     // 注意:不要恢复窗口 alpha,因为遮罩层是主窗口的 alpha,应该保持 0.4f
                     // 只有当主窗口关闭时,才应该恢复窗口 alpha
-                    
+
                     // 嵌套窗口关闭时,恢复主窗口的外部点击关闭功能和引用
                     mainPopupWindow.isOutsideTouchable = wasMainWindowOutsideTouchable
                     popupWindow = savedMainPopupWindow
@@ -879,21 +994,23 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
                 // 保存主 popupWindow 的引用和状态,避免被嵌套窗口覆盖
                 val savedMainPopupWindow = mainPopupWindow
                 val wasMainWindowOutsideTouchable = mainPopupWindow.isOutsideTouchable
-                
+
                 // 临时禁用主窗口的外部点击关闭功能,避免嵌套窗口关闭时主窗口也被关闭
                 mainPopupWindow.isOutsideTouchable = false
-                
-                showDateAndTimePicker(resources.getString(R.string.end_time_select_hint), endTime)
-                
+
+                showDateAndTimePicker(resources.getString(R.string.end_time_select_hint), endTime) {
+                    taskDetail.endTime = endTime.text.toString() + ":00"
+                }
+
                 // showDateAndTimePicker 内部会调用 showPopupWindow,覆盖 popupWindow
                 // showPopupWindow 在 block 执行前就设置了 dismiss 监听器,会将窗口 alpha 恢复为 1f
-                // 我们需要立即重新设置 dismiss 监听器,确保遮罩层(主窗口的 alpha)不被恢复
+                // 重新设置 dismiss 监听器,确保遮罩层(主窗口的 alpha)不被恢复
                 val nestedDatePickerWindow = popupWindow
                 // 立即重新设置 dismiss 监听器,覆盖 showPopupWindow 设置的监听器
                 nestedDatePickerWindow.setOnDismissListener {
                     // 注意:不要恢复窗口 alpha,因为遮罩层是主窗口的 alpha,应该保持 0.4f
                     // 只有当主窗口关闭时,才应该恢复窗口 alpha
-                    
+
                     // 嵌套窗口关闭时,恢复主窗口的外部点击关闭功能和引用
                     mainPopupWindow.isOutsideTouchable = wasMainWindowOutsideTouchable
                     popupWindow = savedMainPopupWindow
@@ -905,10 +1022,42 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
             }
 
             confirm.setOnClickListener {
+
+                if (missionName.text.isNullOrBlank()) {
+                    showMessage(resources.getString(R.string.single_mission_name_input_hint))
+                    return@setOnClickListener
+                }
+
+                if (taskDetail.priorityId == 0) {
+                    showMessage(resources.getString(R.string.priority_select_hint))
+                    return@setOnClickListener
+                }
+
+                if (taskDetail.assignedUserId == 0) {
+                    showMessage(resources.getString(R.string.director_select_hint))
+                    return@setOnClickListener
+                }
+
+                if (startTime.text.isNullOrBlank()) {
+                    showMessage(resources.getString(R.string.start_time_select_hint))
+                    return@setOnClickListener
+                }
+
+                if (endTime.text.isNullOrBlank()) {
+                    showMessage(resources.getString(R.string.end_time_select_hint))
+                    return@setOnClickListener
+                }
+
+                if (hours.text.isNullOrBlank()) {
+                    showMessage(resources.getString(R.string.mission_finish_hours_input_hint))
+                    return@setOnClickListener
+                }
+
                 taskDetail.apply {
                     name = missionName.text.toString()
                     durationHours = hours.text.toString().toDouble()
                     this.remark = remark.getText()
+                    workOrderId = taskId
                 }
 
                 mainPopupWindow.dismiss()
@@ -931,6 +1080,8 @@ class DepartmentProcessDetailActivity : BaseActivity<ActivityDepartmentProcessDe
             }
         }
 
+        Log.e("request body", Gson().toJson(updateDepartmentProcessRequest))
+
         OASystem.apiService.updateDepartmentProcess(updateDepartmentProcessRequest = updateDepartmentProcessRequest)
             .enqueue(object : Callback<BaseResponse> {
                 override fun onResponse(

+ 13 - 0
app/src/main/java/com/pan_american/android/ui/group_management/department_process/DepartmentProcessMissionListFragment.kt

@@ -71,6 +71,19 @@ class DepartmentProcessMissionListFragment :
             missionList.clear()
             getProcessList(2)
         }
+
+        binding.emergencySort.setOnCheckedChangeListener { _, flag ->
+            if (flag) {
+                listRequest.isLv = 1
+            } else {
+                listRequest.isLv = 0
+            }
+
+            listRequest.pageIndex = 1
+            binding.departmentProcessList.adapter!!.notifyItemRangeRemoved(0, missionList.size)
+            missionList.clear()
+            getProcessList(2)
+        }
     }
 
     private fun getProcessList(type: Int) {

+ 393 - 3
app/src/main/java/com/pan_american/android/ui/group_management/department_process/MissionProcessHistoryFragment.kt

@@ -1,8 +1,17 @@
 package com.pan_american.android.ui.group_management.department_process
 
+import android.Manifest
+import android.content.ActivityNotFoundException
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.graphics.Color
+import android.net.Uri
+import android.os.Build
 import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.provider.OpenableColumns
+import android.provider.Settings
 import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
@@ -12,6 +21,11 @@ import android.widget.LinearLayout
 import android.widget.PopupWindow
 import android.widget.RadioButton
 import android.widget.TextView
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.core.content.res.ResourcesCompat
 import androidx.core.net.toUri
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -19,6 +33,7 @@ import com.pan_american.android.OASystem
 import com.pan_american.android.R
 import com.pan_american.android.base.BaseFragment
 import com.pan_american.android.base.BaseResponse
+import com.pan_american.android.base.CardAdapter
 import com.pan_american.android.base.CustomAlertDialog
 import com.pan_american.android.base.ListAdapter
 import com.pan_american.android.data.model.common.entity.FileListItem
@@ -29,6 +44,10 @@ import com.pan_american.android.data.model.group_management.department_process.n
 import com.pan_american.android.data.model.group_management.department_process.network.VerifyMissionRequest
 import com.pan_american.android.databinding.FragmentMissionProcessHistoryBinding
 import com.pan_american.android.util.ScrollEditText
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
 import retrofit2.Call
 import retrofit2.Callback
 import retrofit2.Response
@@ -43,6 +62,21 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
 
     private var receiptList = ArrayList<MissionReceipt>()
 
+    // Activity Result Launcher for file selection
+    private lateinit var filePickerLauncher: ActivityResultLauncher<Intent>
+
+    // Permission launcher
+    private lateinit var permissionLauncher: ActivityResultLauncher<String>
+
+    // Selected files list
+    private val selectedFilesList = ArrayList<FileListItem>()
+
+    // Annex list RecyclerView reference
+    private lateinit var annexListRecyclerView: RecyclerView
+
+    // Annex list adapter reference
+    private lateinit var annexListAdapter: CardAdapter<FileListItem>
+
     override fun getViewBinding(
         inflater: LayoutInflater,
         container: ViewGroup?,
@@ -58,6 +92,44 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
             canProcess = getBoolean("can_process")
         }
 
+        // 注册文件选择器 ActivityResultLauncher(支持多选)
+        filePickerLauncher = registerForActivityResult(
+            ActivityResultContracts.StartActivityForResult()
+        ) { result ->
+            if (result.resultCode == android.app.Activity.RESULT_OK && result.data != null) {
+                // 处理选中的文件(支持多选)
+                val clipData = result.data?.clipData
+                if (clipData != null) {
+                    // 多选文件
+                    val selectedFiles = mutableListOf<Uri>()
+                    for (i in 0 until clipData.itemCount) {
+                        val uri = clipData.getItemAt(i).uri
+                        selectedFiles.add(uri)
+                    }
+                    processSelectedFiles(selectedFiles)
+                } else {
+                    // 单选文件
+                    val selectedFileUri = result.data?.data
+                    selectedFileUri?.let { uri ->
+                        processSelectedFile(uri)
+                    }
+                }
+            }
+        }
+
+        // 注册权限请求 Launcher
+        permissionLauncher = registerForActivityResult(
+            ActivityResultContracts.RequestPermission()
+        ) { isGranted ->
+            if (isGranted) {
+                // 权限被授予,打开文件选择器
+                openFilePicker()
+            } else {
+                // 权限被拒绝,显示提示信息
+                onFileReadPermissionDenied()
+            }
+        }
+
         initViews()
         initEvents()
     }
@@ -90,7 +162,79 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
         }
 
         binding.submit.setOnClickListener {
-            
+
+            val popView = View.inflate(OASystem.context, R.layout.popup_add_mission_history, null)
+
+            popupWindow = PopupWindow(
+                popView,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT
+            )
+
+            showPopupWindow {
+                // 每次打开弹窗时,重置文件列表
+                selectedFilesList.clear()
+                
+                val isMissionFinishedYes: RadioButton =
+                    popView.findViewById(R.id.is_mission_finished_yes)
+                val isMissionFinishedNo: RadioButton =
+                    popView.findViewById(R.id.is_mission_finished_no)
+                val relatedContent: ScrollEditText =
+                    popView.findViewById(R.id.related_content_commit)
+                val selectAnnex: TextView = popView.findViewById(R.id.select_annex)
+                val annexList: RecyclerView = popView.findViewById(R.id.annex_list)
+                val cancel: TextView = popView.findViewById(R.id.cancel)
+                val submit: TextView = popView.findViewById(R.id.submit)
+
+                // 保存 annexList 的引用,以便后续更新
+                annexListRecyclerView = annexList
+
+                // 初始化附件列表(初始为空列表)
+                updateAnnexList()
+
+                selectAnnex.setOnClickListener {
+                    // 检查文件读取权限
+                    checkFileReadPermission()
+                }
+
+                cancel.setOnClickListener {
+                    popupWindow.dismiss()
+                }
+
+                submit.setOnClickListener {
+                    val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).apply {
+                        addFormDataPart("WorkOrderId", missionId.toString())
+                        addFormDataPart("WorkTaskId", stepId.toString())
+                        if (isMissionFinishedYes.isChecked) {
+                            addFormDataPart("IsCompleted", 1.toString())
+                        } else if (isMissionFinishedNo.isChecked) {
+                            addFormDataPart("IsCompleted", 0.toString())
+                        }
+                        addFormDataPart("Content", relatedContent.getText())
+
+                        // 使用 requireContext().contentResolver 来读取文件
+                        for (item in selectedFilesList) {
+                            requireContext().contentResolver.openInputStream(item.uri)
+                                ?.use { inputStream ->
+                                    val content = inputStream.readBytes()
+                                    val fileRequestBody =
+                                        content.toRequestBody(
+                                            "multipart/form-data".toMediaTypeOrNull(),
+                                            0,
+                                            content.size
+                                        )
+                                    addFormDataPart("Files", item.fileName, fileRequestBody)
+                                }
+                        }
+
+                        addFormDataPart("UserId", OASystem.userInfo.userId.toString())
+                    }.build()
+
+                    uploadMissionReceipt(requestBody)
+                }
+            }
+
+            popupWindow.showAtLocation(binding.root, Gravity.CENTER, 0, 0)
         }
     }
 
@@ -145,7 +289,7 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
                     when (data.isApproved) {
                         -1 -> {
                             text = resources.getString(R.string.verify_rejected)
-                            setTextColor(R.color.color_caution)
+                            setTextColor(ResourcesCompat.getColor(resources, R.color.color_caution, null))
                         }
 
                         0 -> {
@@ -207,7 +351,13 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
                 }
 
                 itemView.findViewById<RecyclerView>(R.id.annex_list).apply {
+                    // 设置 layoutManager
                     this.layoutManager = LinearLayoutManager(OASystem.context)
+
+                    // 禁用嵌套滚动,避免与外层 RecyclerView 滚动冲突
+                    isNestedScrollingEnabled = false
+
+                    // 创建 adapter
                     val listAdapter = ListAdapter.Builder<FileListItem>().apply {
                         setData(data.files)
                         setLayoutId(R.layout.item_selector)
@@ -217,6 +367,7 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
                         }
                     }.create()
 
+                    // 设置点击事件
                     listAdapter.onRecyclerViewItemClick =
                         object : ListAdapter.OnRecyclerViewItemClick {
                             override fun onItemClick(position: Int) {
@@ -224,7 +375,7 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
                                     setTitle(resources.getString(R.string.hint))
                                     setMessage(
                                         String.format(
-                                            resources.getString(R.string.document_download),
+                                            resources.getString(R.string.document_file_download_hint),
                                             data.files[position].fileName,
                                             data.files[position].fileName.substringAfter('.')
                                         )
@@ -241,6 +392,9 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
                                 }.show()
                             }
                         }
+
+                    // 关键:将 adapter 设置到 RecyclerView
+                    this.adapter = listAdapter
                 }
 
                 itemView.findViewById<LinearLayout>(R.id.button_group).apply {
@@ -381,4 +535,240 @@ class MissionProcessHistoryFragment : BaseFragment<FragmentMissionProcessHistory
             }
         })
     }
+
+    // 检查文件读取权限
+    private fun checkFileReadPermission() {
+        // Android 13+ 使用 ACTION_GET_CONTENT 不需要存储权限
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            // 直接打开文件选择器
+            openFilePicker()
+        } else {
+            // Android 12 及以下需要 READ_EXTERNAL_STORAGE 权限
+            val permission = Manifest.permission.READ_EXTERNAL_STORAGE
+            if (ContextCompat.checkSelfPermission(
+                    requireContext(),
+                    permission
+                ) == PackageManager.PERMISSION_GRANTED
+            ) {
+                // 已经有权限,打开文件选择器
+                openFilePicker()
+            } else {
+                // 申请文件读取权限
+                permissionLauncher.launch(permission)
+            }
+        }
+    }
+
+    // 文件读取权限被拒绝后的处理
+    private fun onFileReadPermissionDenied() {
+        // 只在 Android 12 及以下版本需要处理权限拒绝
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+            val permission = Manifest.permission.READ_EXTERNAL_STORAGE
+            // 解释为什么需要这个权限
+            if (ActivityCompat.shouldShowRequestPermissionRationale(
+                    requireActivity(),
+                    permission
+                )
+            ) {
+                // 用户拒绝了权限,但没有选择"不再询问"
+                showPermissionExplanationDialog()
+            } else {
+                // 用户拒绝了权限,并选择了"不再询问"
+                showGoToSettingsDialog()
+            }
+        }
+    }
+
+    // 显示权限解释对话框
+    private fun showPermissionExplanationDialog() {
+        CustomAlertDialog.Builder(requireContext())
+            .setTitle(resources.getString(R.string.need_read_extra_strange))
+            .setMessage(resources.getString(R.string.request_strange_permission))
+            .setPositiveButton(resources.getString(R.string.confirm)) { _, _ ->
+                // 再次请求权限(只在 Android 12 及以下)
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+                    permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
+                }
+            }
+            .setNegativeButton(resources.getString(R.string.cancel), null)
+            .show()
+    }
+
+    // 显示前往设置对话框
+    private fun showGoToSettingsDialog() {
+        CustomAlertDialog.Builder(requireContext())
+            .setTitle(resources.getString(R.string.need_read_extra_strange))
+            .setMessage(resources.getString(R.string.reject_permission_hint))
+            .setPositiveButton(resources.getString(R.string.go_to_setting)) { _, _ ->
+                // 打开应用设置页面
+                val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+                val uri = Uri.fromParts("package", requireContext().packageName, null)
+                intent.data = uri
+                startActivity(intent)
+            }
+            .setNegativeButton(resources.getString(R.string.confirm), null)
+            .show()
+    }
+
+    // 打开文件选择器(支持多选)
+    private fun openFilePicker() {
+        val intent = Intent(Intent.ACTION_GET_CONTENT)
+        intent.type = "*/*" // 所有文件类型
+        intent.addCategory(Intent.CATEGORY_OPENABLE)
+        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 启用多选
+
+        try {
+            filePickerLauncher.launch(Intent.createChooser(intent, "选择文件(可多选)"))
+        } catch (_: ActivityNotFoundException) {
+//            Log.e("file_operation", "未找到文件管理器")
+        }
+    }
+
+    // 处理选中的文件(单选)
+    private fun processSelectedFile(fileUri: Uri) {
+        try {
+            val fileName = getFileName(fileUri)
+            val fileItem = FileListItem().apply {
+                this.fileName = fileName
+                this.uri = fileUri  // 保存 Uri 对象,用于后续读取文件
+                this.url = fileUri.toString()  // 保存 Uri 字符串,用于显示
+            }
+            selectedFilesList.add(fileItem)
+            updateAnnexList()
+        } catch (_: Exception) {
+//            Log.e("file_operation", "读取文件失败: ${e.message}")
+        }
+    }
+
+    // 处理选中的多个文件
+    private fun processSelectedFiles(fileUris: List<Uri>) {
+        try {
+            fileUris.forEach { uri ->
+                val fileName = getFileName(uri)
+                val fileItem = FileListItem().apply {
+                    this.fileName = fileName
+                    this.uri = uri  // 保存 Uri 对象,用于后续读取文件
+                    this.url = uri.toString()  // 保存 Uri 字符串,用于显示
+                }
+                selectedFilesList.add(fileItem)
+            }
+            updateAnnexList()
+        } catch (_: Exception) {
+//            Log.e("file_operation", "处理文件失败: ${e.message}")
+        }
+    }
+
+    // 获取文件名
+    private fun getFileName(uri: Uri): String {
+        var fileName = ""
+        val projection = arrayOf(OpenableColumns.DISPLAY_NAME)
+        requireContext().contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
+            if (cursor.moveToFirst()) {
+                val columnIndex = cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
+                fileName = cursor.getString(columnIndex)
+            }
+        }
+        return fileName
+    }
+
+    // 初始化或更新附件列表显示(合并初始化和更新逻辑)
+    private fun updateAnnexList() {
+        // 如果 RecyclerView 还没有初始化,直接返回
+        if (!::annexListRecyclerView.isInitialized) {
+            return
+        }
+
+        val recyclerView = annexListRecyclerView
+
+        // 如果 layoutManager 还没有设置,先设置
+        if (recyclerView.layoutManager == null) {
+            recyclerView.layoutManager = LinearLayoutManager(requireContext())
+        }
+
+        // 创建或更新 adapter
+        annexListAdapter = CardAdapter.Builder<FileListItem>().apply {
+            setData(selectedFilesList)
+            setLayoutId(R.layout.item_delete_button_selector)
+            setCanDelete(true)
+            addBindView { itemView, data ->
+                itemView.findViewById<TextView>(R.id.left_text).text = data.fileName
+            }
+        }.create()
+
+        // 设置删除按钮的点击事件
+        annexListAdapter.onRecyclerViewItemClick = object : CardAdapter.OnRecyclerViewItemClick {
+            override fun onItemClick(position: Int) {
+                // 点击文件项的处理(如果需要)
+            }
+
+            override fun onItemDelete(position: Int) {
+                // 删除文件
+                if (position >= 0 && position < selectedFilesList.size) {
+                    selectedFilesList.removeAt(position)
+                    // 使用 Handler 延迟更新,避免在 RecyclerView 布局过程中更新导致数据不一致
+                    Handler(Looper.getMainLooper()).post {
+                        updateAnnexList() // 重新更新列表
+                    }
+                }
+            }
+        }
+
+        recyclerView.adapter = annexListAdapter
+
+//        Log.e("file_operation", "已选择 ${selectedFilesList.size} 个文件")
+//        for (file in selectedFilesList) {
+//            Log.e("file_operation", "文件: ${file.fileName}")
+//        }
+    }
+
+    private fun uploadMissionReceipt(requestBody: RequestBody) {
+        OASystem.apiService.uploadMissionReceipt(requestBody)
+            .enqueue(object : Callback<BaseResponse> {
+                override fun onResponse(
+                    call: Call<BaseResponse?>,
+                    response: Response<BaseResponse?>
+                ) {
+                    val uploadResponse = response.body()
+
+                    if (uploadResponse != null) {
+
+                        if (uploadResponse.code == 200) {
+                            showMessage(resources.getString(R.string.upload_success))
+
+                            // 1. 关闭 popupWindow
+                            popupWindow.dismiss()
+
+                            // 2. 清空 RecyclerView 的数据展示(先清空 receiptList,然后重新创建 adapter)
+                            receiptList.clear()
+                            
+                            // 重新创建 adapter 并设置空列表,清空显示
+                            val emptyAdapter = ListAdapter.Builder<MissionReceipt>().apply {
+                                setData(receiptList) // receiptList 已经清空
+                                setLayoutId(R.layout.item_mission_receipt)
+                                addBindView { _, _ ->
+                                    // 空实现,因为列表为空,不会调用
+                                }
+                            }.create()
+                            binding.historyList.adapter = emptyAdapter
+
+                            // 3. 执行 getMissionProcessHistory() 重新获取数据
+                            getMissionProcessHistory()
+
+                        } else {
+                            showMessage(resources.getString(R.string.upload_failed))
+                        }
+
+                    } else {
+                        showErrorInfo(R.string.interface_request_error)
+                    }
+                }
+
+                override fun onFailure(
+                    call: Call<BaseResponse?>,
+                    t: Throwable
+                ) {
+                    showErrorInfo(R.string.network_error)
+                }
+            })
+    }
 }

+ 0 - 4
app/src/main/java/com/pan_american/android/ui/group_management/department_process/MissionProcessListFragment.kt

@@ -1,7 +1,6 @@
 package com.pan_american.android.ui.group_management.department_process
 
 import android.os.Bundle
-import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -9,7 +8,6 @@ import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
 import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.gson.Gson
 import com.pan_american.android.OASystem
 import com.pan_american.android.R
 import com.pan_american.android.base.BaseFragment
@@ -127,8 +125,6 @@ class MissionProcessListFragment : BaseFragment<FragmentMissionProcessListBindin
             }
         }
 
-        Log.e("list", Gson().toJson(missionProcessList))
-
         val mainLayoutManager = LinearLayoutManager(OASystem.context)
         val extraLayoutManager = LinearLayoutManager(OASystem.context)
 

+ 11 - 20
app/src/main/java/com/pan_american/android/ui/group_visa/visa_process/VisaProcessDetailActivity.kt

@@ -172,8 +172,8 @@ class VisaProcessDetailActivity : BaseActivity<ActivityVisaProcessDetailBinding>
                         // 将 LinkedTreeMap 转换为 Step7Type
                         val jsonString = Gson().toJson(step7Value)
                         Gson().fromJson(jsonString, Step7Type::class.java)
-                    } catch (e: Exception) {
-                        Log.e("DEBUG", "转换 Step7Type 失败: ${e.message}")
+                    } catch (_: Exception) {
+//                        Log.e("DEBUG", "转换 Step7Type 失败: ${e.message}")
                         Step7Type(false, "")
                     }
                 }
@@ -426,15 +426,6 @@ class VisaProcessDetailActivity : BaseActivity<ActivityVisaProcessDetailBinding>
 
     private fun updateVisaProcessStep(stepContents: Array<String>, stepId: Int) {
 
-        Log.e(
-            "upload", Gson().toJson(
-                VisaProcessUpdateStepRequest(
-                    stepContents = stepContents,
-                    stepId = stepId
-                )
-            )
-        )
-
         OASystem.apiService.updateVisaProcessStep(
             VisaProcessUpdateStepRequest(
                 stepContents = stepContents,
@@ -449,9 +440,9 @@ class VisaProcessDetailActivity : BaseActivity<ActivityVisaProcessDetailBinding>
 
                 if (updateResponse != null) {
                     if (updateResponse.code == 200) {
-                        Log.e("update_info", "update Success")
+                        showMessage(resources.getString(R.string.update_success))
                     } else {
-                        Log.e("update_info", "update Error")
+                        showErrorInfo(R.string.update_error)
                     }
                 }
             }
@@ -460,7 +451,7 @@ class VisaProcessDetailActivity : BaseActivity<ActivityVisaProcessDetailBinding>
                 call: Call<BaseResponse?>,
                 t: Throwable
             ) {
-                showErrorInfo(R.string.update_error)
+                showErrorInfo(R.string.network_error)
             }
         })
     }
@@ -654,7 +645,7 @@ class VisaProcessDetailActivity : BaseActivity<ActivityVisaProcessDetailBinding>
     // 处理选中的多个文件
     private fun processSelectedFiles(fileUris: List<Uri>) {
         try {
-            Log.e("file_operation", "已选择 ${fileUris.size} 个文件")
+//            Log.e("file_operation", "已选择 ${fileUris.size} 个文件")
 
             var nameList = ""
 
@@ -674,12 +665,12 @@ class VisaProcessDetailActivity : BaseActivity<ActivityVisaProcessDetailBinding>
                     val inputStream = contentResolver.openInputStream(uri)
                     inputStream?.use { stream ->
                         // 处理每个文件流
-                        Log.e("file_operation", "处理文件 ${index + 1}: $uri")
+//                        Log.e("file_operation", "处理文件 ${index + 1}: $uri")
 
                         fileStreamList.add(stream.toString())
                     }
-                } catch (e: Exception) {
-                    Log.e("file_operation", "读取文件 ${index + 1} 失败: ${e.message}")
+                } catch (_: Exception) {
+//                    Log.e("file_operation", "读取文件 ${index + 1} 失败: ${e.message}")
                 }
             }
 
@@ -704,8 +695,8 @@ class VisaProcessDetailActivity : BaseActivity<ActivityVisaProcessDetailBinding>
                 }
             }.show()
 
-        } catch (e: Exception) {
-            Log.e("file_operation", "处理文件失败: ${e.message}")
+        } catch (_: Exception) {
+//            Log.e("file_operation", "处理文件失败: ${e.message}")
         }
     }
 

+ 9 - 0
app/src/main/res/layout/fragment_department_process_mission_list.xml

@@ -37,6 +37,15 @@
 
     </LinearLayout>
 
+    <CheckBox
+        android:id="@+id/emergency_sort"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:layout_marginEnd="@dimen/dp_10"
+        android:text="@string/emergency_sort"
+        android:theme="@style/CheckBoxTheme"/>
+
     <com.scwang.smart.refresh.layout.SmartRefreshLayout
         android:id="@+id/department_process_container"
         android:layout_width="match_parent"

+ 184 - 0
app/src/main/res/layout/popup_add_mission_history.xml

@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="@dimen/dp_10"
+    android:layout_marginBottom="@dimen/dp_10"
+    android:background="@drawable/shape_corner_stroke_white"
+    android:orientation="vertical"
+    tools:viewBindingIgnore="true">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/dp_10"
+        android:orientation="vertical"
+        tools:ignore="UselessParent">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/dp_10"
+            android:baselineAligned="false"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:text="@string/is_mission_finished"
+                android:textSize="@dimen/text_size_16_sp"
+                android:textStyle="bold" />
+
+            <RadioGroup
+                android:id="@+id/is_mission_finished"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:gravity="end"
+                android:orientation="horizontal">
+
+                <RadioButton
+                    android:id="@+id/is_mission_finished_yes"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/style_radio_button"
+                    android:buttonTint="@color/title_background_color"
+                    android:checked="true"
+                    android:text="@string/yes"
+                    android:textColor="@color/text_color" />
+
+                <RadioButton
+                    android:id="@+id/is_mission_finished_no"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="@dimen/dp_10"
+                    android:background="@drawable/style_radio_button"
+                    android:buttonTint="@color/title_background_color"
+                    android:text="@string/no"
+                    android:textColor="@color/text_color" />
+            </RadioGroup>
+
+        </LinearLayout>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/line_width"
+            android:layout_marginTop="@dimen/dp_20"
+            android:layout_marginBottom="@dimen/dp_10"
+            android:background="@color/line_color" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/dp_10"
+            android:baselineAligned="false"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="start"
+                android:text="@string/related_content_commit"
+                android:textSize="@dimen/text_size_16_sp"
+                android:textStyle="bold" />
+
+            <com.pan_american.android.util.ScrollEditText
+                android:id="@+id/related_content_commit"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/height_100_dp"
+                android:layout_marginTop="@dimen/dp_10"
+                android:background="@color/white"
+                android:gravity="end"
+                android:singleLine="true"
+                android:textSize="@dimen/text_size_16_sp" />
+
+        </LinearLayout>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/line_width"
+            android:layout_marginTop="@dimen/dp_20"
+            android:layout_marginBottom="@dimen/dp_10"
+            android:background="@color/line_color" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/dp_10"
+            android:layout_marginBottom="@dimen/dp_20"
+            android:baselineAligned="false"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:text="@string/annex"
+                    android:textSize="@dimen/text_size_16_sp"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/select_annex"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/shape_corner_solid_blue"
+                    android:padding="@dimen/dp_5"
+                    android:text="@string/select_annex"
+                    android:textColor="@color/white"
+                    android:textSize="@dimen/text_size_16_sp"
+                    android:textStyle="bold" />
+
+            </LinearLayout>
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/annex_list"
+                android:layout_width="match_parent"
+                android:layout_height="150dp"
+                android:layout_marginTop="@dimen/dp_10" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/button_group"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/cancel"
+                android:layout_width="0dp"
+                android:layout_height="@dimen/height_40_dp"
+                android:layout_margin="@dimen/dp_10"
+                android:layout_weight="1"
+                android:background="@drawable/shape_corner_reset_button"
+                android:gravity="center"
+                android:text="@string/cancel"
+                android:textColor="@color/white"
+                android:textSize="@dimen/text_size_16_sp"
+                android:textStyle="bold" />
+
+            <TextView
+                android:id="@+id/submit"
+                android:layout_width="0dp"
+                android:layout_height="@dimen/height_40_dp"
+                android:layout_margin="@dimen/dp_10"
+                android:layout_weight="1"
+                android:background="@drawable/shape_corner_solid_blue"
+                android:gravity="center"
+                android:text="@string/submit"
+                android:textColor="@color/white"
+                android:textSize="@dimen/text_size_16_sp"
+                android:textStyle="bold" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>

+ 9 - 0
app/src/main/res/values/strings.xml

@@ -1520,6 +1520,8 @@
     <string name="mission_list">任务列表</string>
     <string name="mission_detail">任务详情</string>
 
+    <string name="emergency_sort">加急排序</string>
+    
     <string name="main_mission">主要任务</string>
     <string name="extra_mission">额外任务</string>
 
@@ -1542,6 +1544,13 @@
     <string name="no_receipt_pass_hint">是否无记录通过该项任务?</string>
     <string name="receipt_pass_hint">是否审核通过该项记录?</string>
 
+    <string name="process_name_input_hint">请输入工单名称</string>
+    <string name="foreign_affairs_select_hint">请选择外办选项</string>
+
+    <string name="single_mission_name_input_hint">请输入单项任务名称</string>
+    <string name="priority_select_hint">请选择优先级</string>
+    <string name="mission_finish_hours_input_hint">请输入任务完成小时数</string>
+
     <!-- 签证流程 -->
     <string name="visa_process">签证流程</string>
     <string name="visa_process_group_list_get_error">获取签证流程团组列表失败</string>