18034727885
2026-06-23 56fedc881e978953c3b0636b93c6e25852efa190
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
package com.xw.yunwei.ui.task.accident.handle
 
import android.app.Application
import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.map
import com.blankj.utilcode.util.GsonUtils
import com.xw.common.base.BaseViewModel
import com.xw.common.base.FileSelectBean
import com.xw.common.base.ImageEventListener
import com.xw.common.base.ImageSelectAdapter
import com.xw.common.utils.ToastUtilCommon
import com.xw.yunwei.R
import com.xw.yunwei.model.Category
import com.xw.yunwei.model.Item
import com.xw.yunwei.net.CommonRepository
import com.xw.yunwei.net.TaskRepository
import com.xw.yunwei.ui.MainActivity
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import org.koin.core.component.inject
import java.io.File
 
 
/**
 * 事故处理页面 ViewModel
 *
 * 负责事故处理页面的业务逻辑与数据管理,包括:
 * - 人员伤亡计数(死亡、重伤、轻伤)的 LiveData 维护与增减操作
 * - 现场情况、处置情况文本及字数统计
 * - 责任判定选项与选中状态
 * - 三组图片适配器与图片事件监听的管理
 * - 文件上传(递归上传本地图片到服务器)
 * - 事故处理提交
 */
class AccidentProcessVM(myApplication: Application) : BaseViewModel(myApplication) {
    val commonRepository: CommonRepository by inject()
    val taskRepository: TaskRepository by inject()
 
    /** 当前任务ID */
    var taskId = 0L
 
    /**
     * 任务类型ID 组id|子id1, 子id2, 子id3...
     */
    var taskTypeId: String? = null
 
    /** 死亡人数 LiveData,默认 0 */
    val death = MutableLiveData("0")
 
    /** 重伤人数 LiveData,默认 0 */
    val badlyHurt = MutableLiveData("0")
 
    /** 轻伤人数 LiveData,默认 0 */
    val minorInjuries = MutableLiveData("0")
 
    /** 现场情况描述 LiveData */
    val siteDesc = MutableLiveData("")
 
    /** 处置情况描述 LiveData */
    val disposalDesc = MutableLiveData("")
 
    /** 现场情况当前字数 / 200 */
    val siteNums = siteDesc.map { "${it.length}/200" }
 
    /** 处置情况当前字数 / 200 */
    val disposalNums = disposalDesc.map { "${it.length}/200" }
 
    /** 责任判定选项列表 */
    val dataZrpd = listOf("我方主责", "我方无责", "双方同责")
 
    /** 当前选中的责任判定 */
    var selectZrpd = ""
 
    /** 三组图片选择适配器(对应交警判责、现场情况、处置情况) */
    val adapters = mutableListOf<ImageSelectAdapter>()
 
    /** 三组图片的事件监听器 */
    val imageEvents = mutableListOf<ImageEventListener<FileSelectBean>>()
 
    /** 每组图片最大可上传数量,默认均为 8 张 */
    val maxSizes = listOf(8, 8, 8)
 
    /** 当前正在操作的图片组下标(0/1/2),-1 表示未选择 */
    var operateIndex = -1
 
    /** 事故类型分类列表(大类 + 子类) */
    var categories = mutableListOf<Category>()
 
    /** 页面点击事件统一处理:增减人数按钮、提交按钮 */
    var onClick = View.OnClickListener {
        when (it.id) {
            R.id.cut1 -> death.cut()
            R.id.cut2 -> badlyHurt.cut()
            R.id.cut3 -> minorInjuries.cut()
            R.id.add1 -> death.add()
            R.id.add2 -> badlyHurt.add()
            R.id.add3 -> minorInjuries.add()
            R.id.tvControl -> submit()
        }
    }
 
    /**
     * 提交事故处理信息
     *
     * 校验逻辑:
     * 1. 必须选择责任判定
     * 2. 必须上传交警判责照片
     * 3. 现场情况(文字或照片)至少填一项
     * 4. 处置情况(文字或照片)至少填一项
     *
     * 校验通过后先递归上传本地图片,再调用 [TaskRepository.handleVehicleAccident] 提交。
     */
    private fun submit() {
//        if (selectZrpd.isEmpty()) {
//            ToastUtilCommon.showWarn("请选择责任判断")
//            return
//        }
//        if(adapters[0].data.isEmpty()){
//            ToastUtilCommon.showWarn("请上传交警判责照片")
//            return
//        }
        if (siteDesc.value.isNullOrEmpty() && adapters[1].data.isEmpty()) {
            ToastUtilCommon.showWarn("请填写现场情况或上传现场情况照片")
            return
        }
        if (disposalDesc.value.isNullOrEmpty() && adapters[2].data.isEmpty()) {
            ToastUtilCommon.showWarn("请填写处置情况或上传处置情况照片")
            return
        }
        val files = mutableListOf<FileSelectBean>()
        adapters.forEach {
            files.addAll(it.data)
        }
        showDialog("上传中...")
        uploadFile(files) {
            dismissDialog()
            launchRequest("提交中") {
                val map = mutableMapOf<String, Any?>()
                map["id"] = taskId
                map["deathCount"] = death.value
                map["severeCount"] = badlyHurt.value
                map["minorCount"] = minorInjuries.value
                map["situationDescription"] = siteDesc.value
                map["disposalDescription"] = disposalDesc.value
                map["responsibilityJudgment"] = dataZrpd.indexOf(selectZrpd) + 1
                map["responsibleImage"] = "${adapters[0].data.map { it.url }}"
                map["situationImage"] = "${adapters[1].data.map { it.url }}"
                map["disposalImage"] = "${adapters[2].data.map { it.url }}"
                map["taskTypeId"] = getSelectType()
                taskRepository.handleVehicleAccident(map)
                ToastUtilCommon.showSuc("提交成功,事故已归档")
 
                startActivity(MainActivity::class.java)
            }
        }
 
    }
 
    /**
     * 递归上传本地图片文件到服务器
     *
     * 遍历 [files] 列表,找到第一个 [FileSelectBean.url] 为空的本地文件进行上传,
     * 上传成功后将服务器返回的 URL 写入 [FileSelectBean.url],再继续递归处理下一个。
     *
     * @param files 待上传的文件列表
     * @param succ 全部上传完成后的回调
     */
    private fun uploadFile(files: List<FileSelectBean>, succ: () -> Unit) {
        val result = files.find { it.url.isEmpty() }
        if (result == null) {
            succ()
        } else {
            launchRequest(showLoading = false, error = { _, errMsg ->
                ToastUtilCommon.showError(errMsg)
                dismissDialog()
            }) {
                val file = File(result.localPath)
                val fileRequestBody = file.asRequestBody("image/jpg".toMediaType())
                val mBuilder = MultipartBody.Builder()
                mBuilder.addFormDataPart("file", file.name, fileRequestBody)
                mBuilder.setType(MultipartBody.FORM)
                val data = commonRepository.uploadFile(mBuilder.build())
                result.url = GsonUtils.toJson(data)
                uploadFile(files, succ)
            }
        }
    }
 
    /**
     * MutableLiveData<String> 扩展:人数减 1
     *
     * 当前值小于等于 0 时给出提示,不再递减。
     */
    fun MutableLiveData<String>.cut() {
        val current = value?.toInt() ?: 0
        if (current <= 0) {
            ToastUtilCommon.showWarn("不能再减了")
        } else {
            value = (current - 1).toString()
        }
    }
 
    /**
     * MutableLiveData<String> 扩展:人数加 1
     *
     * 当前值大于等于 99 时给出提示,不再递增。
     */
    fun MutableLiveData<String>.add() {
        val current = value?.toInt() ?: 0
        if (current >= 99) {
            ToastUtilCommon.showWarn("不能再加了")
        } else {
            value = (current + 1).toString()
        }
    }
 
    /**
     * 获取事故类型列表
     *
     * 若 [categories] 已存在则直接回调,否则通过网络请求拉取并转换数据结构。
     *
     * @param block 数据就绪后的回调(在主线程执行)
     */
    fun getTypeList(block: () -> Unit) {
        if (categories.isNotEmpty()) {
            block()
        } else {
            launchRequest {
                taskRepository.getTypeList()?.also { it ->
                    runOnMainThread {
                        categories.clear()
                        it.mapIndexed { index, type ->
                            val items = mutableListOf<Item>()
                            type.child?.forEach {
                                items.add(Item(it.id ?: 0L, it.errorName ?: ""))
                            }
                            categories.add(Category(index, type.errorName ?: "", items))
                        }
                        val ids = taskTypeId?.split(", ") ?: emptyList()
                        categories.forEach { category ->
                            category.items.forEach {
                                it.isSelected = ids.contains(it.id.toString())
                            }
                        }
                        block()
                    }
                }
            }
        }
    }
 
    /**
     * 获取当前已选中的事故类型 ID 字符串
     *
     * 遍历所有分类的子项,过滤出 [Item.isSelected] 为 true 的项,
     * 将其 ID 以逗号分隔拼接成字符串返回。
     *
     * @return 已选中事故类型 ID 的逗号分隔字符串,未选择时返回空串
     */
    fun getSelectType(): String {
        val items = mutableListOf<Item>()
        categories.forEach { items.addAll(it.items) }
        return items.filter { it.isSelected }.map { it.id }.joinToString()
    }
}