From cab9eb335e5e5ef4bc687451e3b29590fef07189 Mon Sep 17 00:00:00 2001
From: 18034727885 <973609879@qq.com>
Date: 星期三, 15 四月 2026 19:15:58 +0800
Subject: [PATCH] 站点管理优化 新增站点异常上报页

---
 app/src/main/res/mipmap-xhdpi/ic_add_picture.png                                |    0 
 app/src/main/res/layout/activity_site_error_upload.xml                          |  217 +++++++++++++++++
 common/src/main/res/values/colors.xml                                           |    3 
 app/src/main/java/com/xw/yunwei/net/WorkRepository.kt                           |   17 +
 app/src/main/res/drawable/shape_gray11_bg_4.xml                                 |    5 
 app/src/main/java/com/xw/yunwei/net/CommonApi.kt                                |    2 
 app/src/main/java/com/xw/yunwei/ui/work/site/SiteNavActivity.kt                 |   17 
 app/src/main/res/mipmap-xhdpi/icon_circle_line_black.png                        |    0 
 app/src/main/java/com/xw/yunwei/model/SiteModel.kt                              |    5 
 app/src/main/res/layout/activity_site_nav.xml                                   |    8 
 app/src/main/res/layout/item_picture_operation.xml                              |   23 +
 common/src/main/java/com/xw/common/utils/GlideEngine.java                       |   25 ++
 app/src/main/java/com/xw/yunwei/net/WorkApi.kt                                  |    4 
 app/src/main/java/com/xw/yunwei/ui/work/site/viewmodel/SiteErrorUploadVM.kt     |  194 ++++++++++++++++
 app/src/main/java/com/xw/yunwei/ui/work/site/adapter/PictureOperationAdapter.kt |   29 ++
 app/src/main/java/com/xw/yunwei/ui/work/site/SiteErrorUploadActivity.kt         |  119 ++++++++-
 16 files changed, 634 insertions(+), 34 deletions(-)

diff --git a/app/src/main/java/com/xw/yunwei/model/SiteModel.kt b/app/src/main/java/com/xw/yunwei/model/SiteModel.kt
index 836e582..356779f 100644
--- a/app/src/main/java/com/xw/yunwei/model/SiteModel.kt
+++ b/app/src/main/java/com/xw/yunwei/model/SiteModel.kt
@@ -1,10 +1,13 @@
 package com.xw.yunwei.model
 
+import android.os.Parcelable
 import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
 
 /**
  * 点位管理数据类
  */
+@Parcelize
 data class SiteModel(
 
     /**
@@ -151,4 +154,4 @@
      */
     @SerializedName("distance")
     val distance: Double? = null
-)
\ No newline at end of file
+) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/xw/yunwei/net/CommonApi.kt b/app/src/main/java/com/xw/yunwei/net/CommonApi.kt
index f12a8a5..d83e797 100644
--- a/app/src/main/java/com/xw/yunwei/net/CommonApi.kt
+++ b/app/src/main/java/com/xw/yunwei/net/CommonApi.kt
@@ -10,6 +10,6 @@
 interface CommonApi {
 
     /*文件上传*/
-    @POST("operation/base/v1/oss/upload")
+    @POST("operation/maintenance-app/base/v1/oss/upload")
     fun uploadFile(@Body body: MultipartBody): Call<HttpResponse<UploadModel>>
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/xw/yunwei/net/WorkApi.kt b/app/src/main/java/com/xw/yunwei/net/WorkApi.kt
index 3bfd756..bd6fd63 100644
--- a/app/src/main/java/com/xw/yunwei/net/WorkApi.kt
+++ b/app/src/main/java/com/xw/yunwei/net/WorkApi.kt
@@ -135,5 +135,7 @@
     @GET("map/maintenance-app/api/v1/fence/listByCityCode")
     fun getFenceByCode(@Query("cityCode") cityCode: String): Call<HttpResponse<List<FenceInfoModel>>>
 
-
+    /* 现场情况上报 */
+    @POST("operation/maintenance-app/api/v1/pointSituationReport/addPointSituationReport")
+    fun addPointSituationReport(@Body params: Map<String, Any>): Call<HttpResponse<Any>>
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/xw/yunwei/net/WorkRepository.kt b/app/src/main/java/com/xw/yunwei/net/WorkRepository.kt
index b10501c..81b2eb1 100644
--- a/app/src/main/java/com/xw/yunwei/net/WorkRepository.kt
+++ b/app/src/main/java/com/xw/yunwei/net/WorkRepository.kt
@@ -1,9 +1,11 @@
 package com.xw.yunwei.net
 
+import com.blankj.utilcode.util.GsonUtils
 import com.xw.common.net.BaseHttpManager.Companion.getBody
 import com.xw.common.net.interceptAwait
 import com.xw.yunwei.model.CheckRecordModel
 import com.xw.yunwei.model.MaintainRecordModel
+import com.xw.yunwei.model.SiteModel
 
 class WorkRepository(private val api: WorkApi) {
 
@@ -55,4 +57,19 @@
     suspend fun getFenceByCode(cityCode: String) = api.getFenceByCode(cityCode).interceptAwait()
     suspend fun queryArea() = api.queryArea().interceptAwait()
 
+    /* 现场情况上报 */
+    suspend fun addPointSituationReport(
+        work: Boolean,
+        model: SiteModel,
+        sceneDescription: String,
+        scenePhoto: List<String>,//现场照片JSON数组
+    ): Any? {
+        val map = mapOf<String, Any>(
+            "fieldSituation" to if (work) 1 else 2,//现场情况(1=正常,2=异常)
+            "pointId" to model.id.toString(),//点位id
+            "sceneDescription" to sceneDescription,//现场情况说明
+            "scenePhoto" to GsonUtils.toJson(scenePhoto),
+        )
+        return api.addPointSituationReport(map).interceptAwait()
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/xw/yunwei/ui/work/site/SiteErrorUploadActivity.kt b/app/src/main/java/com/xw/yunwei/ui/work/site/SiteErrorUploadActivity.kt
index cc2bd45..e3c4989 100644
--- a/app/src/main/java/com/xw/yunwei/ui/work/site/SiteErrorUploadActivity.kt
+++ b/app/src/main/java/com/xw/yunwei/ui/work/site/SiteErrorUploadActivity.kt
@@ -1,26 +1,117 @@
 package com.xw.yunwei.ui.work.site
 
 import android.os.Bundle
-import androidx.activity.enableEdgeToEdge
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.view.ViewCompat
-import androidx.core.view.WindowInsetsCompat
+import android.view.View
+import androidx.core.content.ContextCompat
+import androidx.core.os.bundleOf
+import androidx.core.widget.addTextChangedListener
+import androidx.recyclerview.widget.GridLayoutManager
+import com.xw.common.base.BaseActivity
+import com.xw.common.utils.GlideEngine
+import com.xw.yunwei.BR
 import com.xw.yunwei.R
+import com.xw.yunwei.databinding.ActivitySiteErrorUploadBinding
+import com.xw.yunwei.model.SiteModel
+import com.xw.yunwei.ui.work.site.adapter.PictureOperationAdapter
+import com.xw.yunwei.ui.work.site.viewmodel.SiteErrorUploadVM
 
-class SiteErrorUploadActivity : AppCompatActivity() {
-    companion object{
-        fun getParams(params:List<Pair<String,Any>>): Bundle{
-
+class SiteErrorUploadActivity : BaseActivity<ActivitySiteErrorUploadBinding, SiteErrorUploadVM>() {
+    companion object {
+        //获取跳转该页面参数
+        fun getParams(model: SiteModel): Bundle {
+            return bundleOf("model" to model)
         }
     }
+
+    val adapter by lazy {
+        PictureOperationAdapter().apply {
+            addChildClickViewIds(R.id.iv_delete)
+        }
+    }
+
+    override fun getBindingVariable() = BR.viewModel
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        enableEdgeToEdge()
-        setContentView(R.layout.activity_site_error_upload)
-        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
-            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
-            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
-            insets
+        viewModel.model.postValue(intent.getParcelableExtra("model"))
+    }
+
+    override fun setListener() {
+        super.setListener()
+        binding.tvWork.setOnClickListener {
+            viewModel.working.postValue(true)
         }
+        binding.tvError.setOnClickListener {
+            viewModel.working.postValue(false)
+        }
+        binding.etInput.addTextChangedListener {
+            binding.tvNotice.visibility =
+                if ((it?.toString()?.length ?: 0) > viewModel.maxLength.value!!) {
+                    View.VISIBLE
+                } else {
+                    View.GONE
+                }
+            viewModel.countNotice.postValue("${it?.toString()?.length ?: 0}/${viewModel.maxLength.value!!}")
+        }
+        adapter.setOnItemClickListener { _, _, i -> viewModel.operatePicture(this, i) }
+        adapter.setOnItemChildClickListener { _, _, i -> viewModel.removePicture(i) }
+    }
+
+    //设置运行状态
+    private fun setWorkState() {
+        binding.tvWork.setTextColor(
+            ContextCompat.getColor(
+                this,
+                if (viewModel.working.value == true) {
+                    com.xw.common.R.color.green_5
+                } else {
+                    com.xw.common.R.color.gray_9
+                }
+            )
+        )
+        binding.tvWork.setBackgroundResource(
+            if (viewModel.working.value == true) {
+                R.drawable.shape_green_1_bg_4
+            } else {
+                R.drawable.shape_gray11_bg_4
+            }
+        )
+        binding.tvWork.setTextColor(
+            ContextCompat.getColor(
+                this,
+                if (viewModel.working.value == false) {
+                    com.xw.common.R.color.orange_5
+                } else {
+                    com.xw.common.R.color.gray_9
+                }
+            )
+        )
+        binding.tvError.setBackgroundResource(
+            if (viewModel.working.value == false) {
+                R.drawable.shape_orange_1_bg_4
+            } else {
+                R.drawable.shape_gray11_bg_4
+            }
+        )
+    }
+
+    override fun setObservable() {
+        super.setObservable()
+        viewModel.model.observe(this) {
+            GlideEngine.createGlideEngine().load(binding.ivPicture, it.pointPictures, 0, 0, 4f)
+            binding.tvTitle.text = it.pointName
+            binding.tvDetail.text = it.pointAddress
+        }
+        viewModel.operationPictures.observe(this) {
+            adapter.setNewInstance(it)
+        }
+        viewModel.working.observe(this) {
+            setWorkState()
+        }
+    }
+
+    override fun init() {
+        super.init()
+        binding.rvList.adapter = adapter
+        binding.rvList.layoutManager = GridLayoutManager(this, 5)
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/xw/yunwei/ui/work/site/SiteNavActivity.kt b/app/src/main/java/com/xw/yunwei/ui/work/site/SiteNavActivity.kt
index 3c23f16..604fd14 100644
--- a/app/src/main/java/com/xw/yunwei/ui/work/site/SiteNavActivity.kt
+++ b/app/src/main/java/com/xw/yunwei/ui/work/site/SiteNavActivity.kt
@@ -1,6 +1,5 @@
 package com.xw.yunwei.ui.work.site
 
-import com.xw.common.utils.LogUtils
 import android.view.Gravity
 import android.view.KeyEvent
 import android.view.View
@@ -10,7 +9,6 @@
 import android.widget.LinearLayout
 import androidx.core.view.isVisible
 import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.afollestad.materialdialogs.MaterialDialog
 import com.afollestad.materialdialogs.customview.customView
@@ -20,12 +18,10 @@
 import com.xw.common.CommonDialog
 import com.xw.common.R
 import com.xw.common.base.BaseActivity
-import com.xw.common.base.OnItemClickListener
 import com.xw.common.base.OneItemElementClickListener
 import com.xw.common.utils.BindUtils
 import com.xw.common.utils.BindUtils.getUrl
 import com.xw.common.utils.GpsHelper.Companion.navigate
-import com.xw.common.utils.ToastUtilCommon
 import com.xw.common.viewAdapter.ImageDefault
 import com.xw.common.viewAdapter.ViewAdapter
 import com.xw.common.viewAdapter.ViewAdapter.Companion.showAnim
@@ -34,7 +30,6 @@
 import com.xw.yunwei.databinding.DialogSiteFilterBinding
 import com.xw.yunwei.model.SiteModel
 import com.xw.yunwei.ui.work.AMapFragment
-import kotlinx.coroutines.launch
 import org.json.JSONArray
 
 class SiteNavActivity : BaseActivity<ActivitySiteNavBinding, SiteNavVM>(),
@@ -104,7 +99,7 @@
                     areas.add(area)
                 }
             }
-            if(areas.isNotEmpty()){
+            if (areas.isNotEmpty()) {
                 mapFrag.drawPolygon(areas)
             }
         }
@@ -119,7 +114,7 @@
 //                if (viewModel.search.value.isNullOrEmpty()) {
 //                    ToastUtilCommon.show("请输入搜索内容")
 //                } else {
-                    viewModel.query()
+                viewModel.query()
 //                }
             }
             false
@@ -139,7 +134,8 @@
                 CommonDialog.showTipsDialog(
                     this@SiteNavActivity,
                     "您未开启gps权限,无法获取当前定位,请打开后重新操作",
-                    null)
+                    null
+                )
             } else {
                 if (viewModel.fences.value.isNullOrEmpty()) {
                     viewModel.cityCode = location.adCode.substring(0, 4) + "00"
@@ -157,7 +153,10 @@
         }
         binding.btnErrorUpload.setOnClickListener {
             viewModel.siteItem.value?.let {
-
+                startActivity(
+                    SiteErrorUploadActivity::class.java,
+                    SiteErrorUploadActivity.getParams(it)
+                )
             }
         }
     }
diff --git a/app/src/main/java/com/xw/yunwei/ui/work/site/adapter/PictureOperationAdapter.kt b/app/src/main/java/com/xw/yunwei/ui/work/site/adapter/PictureOperationAdapter.kt
new file mode 100644
index 0000000..4db4932
--- /dev/null
+++ b/app/src/main/java/com/xw/yunwei/ui/work/site/adapter/PictureOperationAdapter.kt
@@ -0,0 +1,29 @@
+package com.xw.yunwei.ui.work.site.adapter
+
+import android.widget.ImageView
+import com.chad.library.adapter.base.BaseQuickAdapter
+import com.chad.library.adapter.base.viewholder.BaseViewHolder
+import com.xw.common.utils.GlideEngine
+import com.xw.yunwei.R
+
+//图片操作适配器
+class PictureOperationAdapter() :
+    BaseQuickAdapter<String, BaseViewHolder>(R.layout.item_picture_operation) {
+    override fun convert(
+        holder: BaseViewHolder,
+        item: String
+    ) {
+        val imageView = holder.getView<ImageView>(R.id.iv_picture)
+        if (item.isEmpty()) {
+            imageView.setImageResource(R.mipmap.ic_add_picture)
+        } else {
+            GlideEngine.createGlideEngine().load(
+                imageView,
+                item,
+                0, 0,
+                4f
+            )
+        }
+        holder.setGone(R.id.iv_delete, item.isEmpty())
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/xw/yunwei/ui/work/site/viewmodel/SiteErrorUploadVM.kt b/app/src/main/java/com/xw/yunwei/ui/work/site/viewmodel/SiteErrorUploadVM.kt
new file mode 100644
index 0000000..da9025a
--- /dev/null
+++ b/app/src/main/java/com/xw/yunwei/ui/work/site/viewmodel/SiteErrorUploadVM.kt
@@ -0,0 +1,194 @@
+package com.xw.yunwei.ui.work.site.viewmodel
+
+import android.annotation.SuppressLint
+import android.app.Application
+import android.content.Context
+import android.view.View
+import androidx.core.net.toUri
+import androidx.lifecycle.MutableLiveData
+import com.blankj.utilcode.util.UriUtils
+import com.luck.picture.lib.basic.PictureSelector
+import com.luck.picture.lib.config.PictureMimeType
+import com.luck.picture.lib.config.SelectMimeType
+import com.luck.picture.lib.entity.LocalMedia
+import com.luck.picture.lib.interfaces.OnResultCallbackListener
+import com.xw.common.CommonDialog
+import com.xw.common.base.BaseViewModel
+import com.xw.common.utils.GlideEngine
+import com.xw.common.utils.PictureCompressEngine
+import com.xw.common.utils.ToastUtilCommon
+import com.xw.yunwei.R
+import com.xw.yunwei.model.SiteModel
+import com.xw.yunwei.net.CommonRepository
+import com.xw.yunwei.net.WorkRepository
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.MultipartBody
+import okhttp3.RequestBody.Companion.asRequestBody
+import org.koin.core.component.inject
+import java.io.File
+
+class SiteErrorUploadVM(application: Application) : BaseViewModel(application),
+    OnResultCallbackListener<LocalMedia> {
+    val commonRepository: CommonRepository by inject()
+    val workRepository: WorkRepository by inject()
+
+    val model = MutableLiveData<SiteModel>()
+    var working = MutableLiveData<Boolean>()
+    val maxLength = MutableLiveData(200)
+    val desc = MutableLiveData("")
+    val countNotice: MutableLiveData<String> =
+        MutableLiveData("${desc.value?.length ?: 0}/${maxLength.value}")
+    val noticeLabel: MutableLiveData<String> = MutableLiveData("最多输入${maxLength.value}字")
+    val pictureMax = MutableLiveData(9)
+
+    //操作图片
+    val operationPictures: MutableLiveData<MutableList<String>> = MutableLiveData(mutableListOf(""))
+
+    //是否正在上传中
+    var isUploading = false
+    val onClick = View.OnClickListener {
+        when (it.id) {
+            R.id.btn_error_upload -> submit()
+        }
+    }
+
+    //添加图片
+    fun addPicture(url: String) {
+        val list = operationPictures.value ?: mutableListOf()
+        list.add(url)
+        refreshPictures(list)
+        operationPictures.postValue(list)
+    }
+
+    //移除图片
+    fun removePicture(position: Int) {
+        val list = operationPictures.value ?: mutableListOf()
+        list.removeAt(position)
+        refreshPictures(list)
+        operationPictures.postValue(list)
+    }
+
+    //刷新图片
+    fun refreshPictures(list: MutableList<String>) {
+        list.removeIf { it.isEmpty() }
+        if (list.size == pictureMax.value) {
+            return
+        }
+        list.add("")
+    }
+
+    //操作图片
+    fun operatePicture(context: Context, position: Int) {
+        val list = operationPictures.value ?: mutableListOf()
+        val element = list.elementAtOrNull(position) ?: return
+        if (element.isEmpty()) {
+            operatePictureFromDevice(context)
+        }
+    }
+
+    //从设备操作图片
+    fun operatePictureFromDevice(context: Context) {
+        CommonDialog.showTypeDialog(context) {
+            if (it == 0) takeAPhotograph(context) else if (it == 1) enterGallery(context)
+        }
+    }
+
+    // 进入图库
+    @SuppressLint("SourceLockedOrientationActivity")
+    private fun enterGallery(context: Context) {
+        PictureSelector.create(context)
+            .openGallery(SelectMimeType.ofImage())
+            .setImageEngine(GlideEngine.createGlideEngine())
+            .setMaxSelectNum(1)
+            .setImageSpanCount(4)
+            .isPreviewImage(true)
+            .isDisplayCamera(false)
+            .setCompressEngine(PictureCompressEngine())
+            .forResult(this)
+    }
+
+    // 拍照
+    private fun takeAPhotograph(context: Context) {
+        PictureSelector.create(context)
+            .openCamera(SelectMimeType.ofImage())
+            .setCompressEngine(PictureCompressEngine())
+            .forResultActivity(this)
+    }
+
+    override fun onResult(result: ArrayList<LocalMedia>?) {
+        result?.let { selectList ->
+            for (item in selectList) {
+                val path: String = item.availablePath
+                val imgPath =
+                    if (PictureMimeType.isContent(path) && !item.isCut && !item.isCompressed) {
+                        UriUtils.uri2File(path.toUri()).absolutePath
+                    } else {
+                        path
+                    }
+                addPicture(imgPath)
+            }
+        }
+    }
+
+    override fun onCancel() {
+
+    }
+
+    private fun uploadFile(files: MutableList<String>, succ: () -> Unit) {
+        files.removeIf { it.isEmpty() }
+        val result = files.find { !it.startsWith("http") }
+        if (result == null) {
+            succ()
+        } else {
+            launchRequest(showLoading = false, error = { _, errMsg ->
+                ToastUtilCommon.showError(errMsg)
+                dismissDialog()
+                isUploading = false
+            }) {
+                val file = File(result)
+                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())
+                files[files.indexOf(result)] = data?.url ?: ""
+                uploadFile(files, succ)
+            }
+        }
+    }
+
+    //提交
+    fun submit() {
+        if (working.value == null) {
+            ToastUtilCommon.showSuc("请选择现场情况")
+            return
+        }
+        if ((desc.value?.length ?: 0) > maxLength.value!!) {
+            ToastUtilCommon.showSuc(noticeLabel.value)
+            return
+        }
+        val list = operationPictures.value ?: mutableListOf()
+        list.removeIf { it.isEmpty() }
+        if (list.count { !it.startsWith("http") } > 0) {
+            uploadFile(list) { submit(list) }
+            return
+        }
+        submit(list)
+    }
+
+    //提交
+    private fun submit(urls: MutableList<String>) {
+        launchRequest {
+            workRepository.addPointSituationReport(
+                working.value!!,
+                model.value!!,
+                desc.value ?: "",
+                urls
+            )
+                ?.let {
+                    ToastUtilCommon.showSuc("提交成功")
+                    finish()
+                }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_gray11_bg_4.xml b/app/src/main/res/drawable/shape_gray11_bg_4.xml
new file mode 100644
index 0000000..9ea3c56
--- /dev/null
+++ b/app/src/main/res/drawable/shape_gray11_bg_4.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="@dimen/dp_4" />
+    <solid android:color="@color/gray_11" />
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_site_error_upload.xml b/app/src/main/res/layout/activity_site_error_upload.xml
index 6054c4d..91fccc0 100644
--- a/app/src/main/res/layout/activity_site_error_upload.xml
+++ b/app/src/main/res/layout/activity_site_error_upload.xml
@@ -1,10 +1,213 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/main"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".ui.work.site.SiteErrorUploadActivity">
+    xmlns:tools="http://schemas.android.com/tools">
 
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+    <data>
+
+        <variable
+            name="viewModel"
+            type="com.xw.yunwei.ui.work.site.viewmodel.SiteErrorUploadVM" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/white">
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/back"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/dp_20"
+            android:layout_marginTop="@dimen/dp_56"
+            android:onClick="@{viewModel::onBack}"
+            android:src="@drawable/svg_back"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="现场情况上报"
+            android:textColor="@color/gray_10"
+            android:textSize="@dimen/sp_16"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="@+id/back"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="@+id/back" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginTop="@dimen/dp_88"
+            android:fillViewport="true"
+            android:orientation="vertical">
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_marginTop="@dimen/dp_12"
+                android:layout_weight="1">
+
+                <ImageView
+                    android:id="@+id/iv_picture"
+                    android:layout_width="@dimen/dp_68"
+                    android:layout_height="@dimen/dp_92"
+                    android:layout_marginStart="@dimen/dp_20"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <TextView
+                    android:id="@+id/tv_title"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="@dimen/dp_8"
+                    android:layout_marginEnd="@dimen/dp_20"
+                    android:textColor="@color/gray_9"
+                    android:textSize="@dimen/sp_14"
+                    android:textStyle="bold"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toEndOf="@id/iv_picture"
+                    app:layout_constraintTop_toTopOf="@id/iv_picture" />
+
+                <TextView
+                    android:id="@+id/tv_detail"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="@dimen/dp_8"
+                    android:layout_marginTop="@dimen/dp_4"
+                    android:layout_marginEnd="@dimen/dp_20"
+                    android:textColor="@color/color_838fa6"
+                    android:textSize="@dimen/sp_14"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toEndOf="@id/iv_picture"
+                    app:layout_constraintTop_toBottomOf="@id/tv_title" />
+
+                <TextView
+                    android:id="@+id/tv_label"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="@dimen/dp_20"
+                    android:layout_marginTop="@dimen/dp_20"
+                    android:text="现场情况"
+                    android:textColor="@color/gray_9"
+                    android:textSize="@dimen/sp_16"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/iv_picture" />
+
+                <TextView
+                    android:id="@+id/tv_work"
+                    android:layout_width="0dp"
+                    android:layout_height="@dimen/dp_36"
+                    android:layout_marginStart="@dimen/dp_20"
+                    android:layout_marginTop="@dimen/dp_12"
+                    android:layout_marginEnd="5dp"
+                    android:background="@drawable/shape_gray11_bg_4"
+                    android:gravity="center"
+                    android:text="正常"
+                    android:textColor="@color/gray_9"
+                    android:textSize="@dimen/sp_14"
+                    android:textStyle="bold"
+                    app:layout_constraintEnd_toStartOf="@id/tv_error"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/tv_label" />
+
+                <TextView
+                    android:id="@+id/tv_error"
+                    android:layout_width="0dp"
+                    android:layout_height="@dimen/dp_36"
+                    android:layout_marginStart="@dimen/dp_5"
+                    android:layout_marginTop="@dimen/dp_12"
+                    android:layout_marginEnd="@dimen/dp_20"
+                    android:background="@drawable/shape_gray11_bg_4"
+                    android:gravity="center"
+                    android:text="异常"
+                    android:textColor="@color/gray_9"
+                    android:textSize="@dimen/sp_14"
+                    android:textStyle="bold"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toEndOf="@id/tv_work"
+                    app:layout_constraintTop_toBottomOf="@id/tv_label" />
+
+                <EditText
+                    android:id="@+id/et_input"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginHorizontal="@dimen/dp_20"
+                    android:layout_marginTop="@dimen/dp_12"
+                    android:background="@drawable/shape_gray_3_bg_4"
+                    android:gravity="top|start"
+                    android:hint="请输入现场情况描述,最多可输入200字"
+                    android:minHeight="@dimen/dp_91"
+                    android:padding="@dimen/dp_12"
+                    android:text="@={viewModel.desc}"
+                    android:textColor="@color/gray_9"
+                    android:textColorHint="@color/gray_6"
+                    android:textSize="@dimen/sp_14"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/tv_work" />
+
+                <TextView
+                    android:id="@+id/tv_count"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginEnd="@dimen/dp_12"
+                    android:layout_marginBottom="@dimen/dp_12"
+                    android:text="@{viewModel.countNotice}"
+                    android:textColor="@color/gray_6"
+                    android:textSize="@dimen/sp_11"
+                    app:layout_constraintBottom_toBottomOf="@id/et_input"
+                    app:layout_constraintEnd_toEndOf="@id/et_input" />
+
+                <TextView
+                    android:id="@+id/tv_notice"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/dp_4"
+                    android:text="@={viewModel.noticeLabel}"
+                    android:textColor="@color/color_ec5340"
+                    android:textSize="@dimen/sp_11"
+                    android:visibility="gone"
+                    app:layout_constraintStart_toStartOf="@id/et_input"
+                    app:layout_constraintTop_toBottomOf="@id/et_input"
+                    tools:visibility="visible" />
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/rv_list"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginHorizontal="@dimen/dp_17"
+                    android:layout_marginTop="@dimen/dp_9"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/tv_notice"
+                    tools:itemCount="5"
+                    tools:layoutManager="GridLayoutManager"
+                    tools:listitem="@layout/item_picture_operation"
+                    tools:spanCount="5" />
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1px"
+                android:background="@color/gray_4" />
+
+            <com.google.android.material.button.MaterialButton
+                android:id="@+id/btn_error_upload"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="@dimen/dp_20"
+                android:layout_marginVertical="@dimen/dp_12"
+                android:backgroundTint="@color/blue_5"
+                android:text="提交"
+                android:onClick="@{viewModel.onClick}"
+                android:textSize="@dimen/sp_16"
+                android:textStyle="bold"
+                app:cornerRadius="@dimen/dp_4" />
+        </LinearLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_site_nav.xml b/app/src/main/res/layout/activity_site_nav.xml
index fea5ed2..54a7e1a 100644
--- a/app/src/main/res/layout/activity_site_nav.xml
+++ b/app/src/main/res/layout/activity_site_nav.xml
@@ -363,12 +363,18 @@
                                     android:textColor="@color/gray_7"
                                     android:textSize="@dimen/sp_14" />
 
+                                <View
+                                    android:layout_width="match_parent"
+                                    android:layout_height="1px"
+                                    android:layout_marginTop="@dimen/dp_38"
+                                    android:background="@color/gray_4" />
+
                                 <com.google.android.material.button.MaterialButton
                                     android:id="@+id/btn_error_upload"
                                     android:layout_width="match_parent"
                                     android:layout_height="wrap_content"
                                     android:layout_marginHorizontal="@dimen/dp_20"
-                                    android:layout_marginTop="@dimen/dp_50"
+                                    android:layout_marginVertical="@dimen/dp_12"
                                     android:backgroundTint="@color/blue_5"
                                     android:text="现场情况上报"
                                     android:textSize="@dimen/sp_16"
diff --git a/app/src/main/res/layout/item_picture_operation.xml b/app/src/main/res/layout/item_picture_operation.xml
new file mode 100644
index 0000000..74b8566
--- /dev/null
+++ b/app/src/main/res/layout/item_picture_operation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="@dimen/dp_5">
+
+    <ImageView
+        android:id="@+id/iv_picture"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@mipmap/ic_add_picture" />
+
+    <ImageView
+        android:id="@+id/iv_delete"
+        android:layout_width="@dimen/dp_24"
+        android:layout_height="@dimen/dp_24"
+        android:layout_gravity="end"
+        android:padding="@dimen/dp_4"
+        android:src="@mipmap/icon_circle_line_black"
+        android:visibility="gone"
+        tools:visibility="visible" />
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-xhdpi/ic_add_picture.png b/app/src/main/res/mipmap-xhdpi/ic_add_picture.png
new file mode 100644
index 0000000..0d2fc2f
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_add_picture.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/icon_circle_line_black.png b/app/src/main/res/mipmap-xhdpi/icon_circle_line_black.png
new file mode 100644
index 0000000..f5f3b7c
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/icon_circle_line_black.png
Binary files differ
diff --git a/common/src/main/java/com/xw/common/utils/GlideEngine.java b/common/src/main/java/com/xw/common/utils/GlideEngine.java
index f09291d..8dcaaf0 100644
--- a/common/src/main/java/com/xw/common/utils/GlideEngine.java
+++ b/common/src/main/java/com/xw/common/utils/GlideEngine.java
@@ -3,6 +3,9 @@
 import android.content.Context;
 import android.widget.ImageView;
 
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+
 import com.bumptech.glide.Glide;
 import com.bumptech.glide.load.resource.bitmap.CenterCrop;
 import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
@@ -101,6 +104,28 @@
         Glide.with(context).resumeRequests();
     }
 
+    /**
+     * 加载图片
+     *
+     * @param imageView   图片控件
+     * @param url         链接
+     * @param error       加载异常占位图资源id
+     * @param placeHolder 占位图资源id
+     * @param radius      圆角 单位dp
+     */
+    public void load(@NonNull ImageView imageView,
+                     String url,
+                     @IdRes int error, @IdRes int placeHolder,
+                     float radius) {
+        float value = radius * imageView.getContext().getResources().getDisplayMetrics().density;
+        Glide.with(imageView)
+                .load(url)
+                .transform(new CenterCrop(), new RoundedCorners((int) value))
+                .placeholder(placeHolder)
+                .error(error)
+                .into(imageView);
+    }
+
     private GlideEngine() {
     }
 
diff --git a/common/src/main/res/values/colors.xml b/common/src/main/res/values/colors.xml
index bf38d28..de7c9d6 100644
--- a/common/src/main/res/values/colors.xml
+++ b/common/src/main/res/values/colors.xml
@@ -67,6 +67,7 @@
     <color name="gray_8">#DB31456A</color>
     <color name="gray_9">#FF31456A</color>
     <color name="gray_10">#FF0F1521</color>
+    <color name="gray_11">#0831456A</color>
 
     <!-- 中性色语义化别名  -->
     <color name="color_background">@color/gray_1</color>
@@ -79,5 +80,7 @@
 
 
     <color name="color_transparent">#00000000</color>
+    <color name="color_838fa6">#ff838fa6</color>
+    <color name="color_ec5340">#ffec5340</color>
 
 </resources>
\ No newline at end of file

--
Gitblit v1.7.1