18034727885
2026-04-15 cab9eb335e5e5ef4bc687451e3b29590fef07189
站点管理优化
新增站点异常上报页
6个文件已添加
10个文件已修改
668 ■■■■■ 已修改文件
app/src/main/java/com/xw/yunwei/model/SiteModel.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/net/CommonApi.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/net/WorkApi.kt 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/net/WorkRepository.kt 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/ui/work/site/SiteErrorUploadActivity.kt 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/ui/work/site/SiteNavActivity.kt 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/ui/work/site/adapter/PictureOperationAdapter.kt 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/ui/work/site/viewmodel/SiteErrorUploadVM.kt 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/shape_gray11_bg_4.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_site_error_upload.xml 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_site_nav.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/item_picture_operation.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/ic_add_picture.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/icon_circle_line_black.png 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/xw/common/utils/GlideEngine.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/res/values/colors.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
)
) : Parcelable
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>>
}
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>>
}
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()
    }
}
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)
    }
}
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)
                )
            }
        }
    }
app/src/main/java/com/xw/yunwei/ui/work/site/adapter/PictureOperationAdapter.kt
New file
@@ -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())
    }
}
app/src/main/java/com/xw/yunwei/ui/work/site/viewmodel/SiteErrorUploadVM.kt
New file
@@ -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()
                }
        }
    }
}
app/src/main/res/drawable/shape_gray11_bg_4.xml
New file
@@ -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>
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>
    <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>
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"
app/src/main/res/layout/item_picture_operation.xml
New file
@@ -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>
app/src/main/res/mipmap-xhdpi/ic_add_picture.png
app/src/main/res/mipmap-xhdpi/icon_circle_line_black.png
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() {
    }
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>