日报2025521

团队作业开发
完成保养任务派发

package com.example.sanpaias.activity

import MaintenancePlan
import android.app.DatePickerDialog
import android.app.Dialog
import android.app.TimePickerDialog
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.sanpaias.R
import com.example.sanpaias.entity.Device
import com.example.sanpaias.network.RetrofitClient
import com.google.gson.Gson
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import java.text.SimpleDateFormat
import java.util.*

class MaintenancePlanListActivity : AppCompatActivity() {

    private lateinit var rvMaintenancePlans: RecyclerView
    private lateinit var tvNoData: TextView
    private lateinit var btnAddPlan: Button
    private val planList = mutableListOf<MaintenancePlan>()
    private val deviceList = mutableListOf<Device>()
    private lateinit var adapter: MaintenancePlanAdapter
    private val TAG = "MaintenancePlanActivity"
    private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maintenance_plan_list)

        // 设置返回按钮
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        supportActionBar?.title = "保养计划列表"

        // 初始化视图
        rvMaintenancePlans = findViewById(R.id.rvMaintenancePlans)
        tvNoData = findViewById(R.id.tvNoData)
        btnAddPlan = findViewById(R.id.btnAddPlan)

        // 设置RecyclerView
        rvMaintenancePlans.layoutManager = LinearLayoutManager(this)
        adapter = MaintenancePlanAdapter(planList)
        rvMaintenancePlans.adapter = adapter

        // 设置新增计划按钮点击事件
        btnAddPlan.setOnClickListener {
            showAddPlanDialog()
        }

        // 加载设备列表
        loadDevices()

        // 加载保养计划数据
        loadMaintenancePlans()
    }

    private fun loadDevices() {
        lifecycleScope.launch {
            try {
                val response = RetrofitClient.instance.getAllDevices()
                if (response.isSuccessful && response.body()?.code == 200) {
                    deviceList.clear()
                    deviceList.addAll(response.body()?.data ?: emptyList())
                    Log.d(TAG, "加载设备列表成功: ${deviceList.size}个设备")
                } else {
                    Log.e(TAG, "加载设备列表失败: ${response.message()}")
                }
            } catch (e: Exception) {
                Log.e(TAG, "加载设备列表异常", e)
            }
        }
    }

    private fun loadMaintenancePlans() {
        lifecycleScope.launch {
            try {
                val response = RetrofitClient.instance.getMaintenancePlanList()
                if (response.isSuccessful && response.body()?.code == 200) {
                    val plans = response.body()?.data ?: emptyList()
                    if (plans.isNotEmpty()) {
                        planList.clear()
                        planList.addAll(plans)
                        adapter.notifyDataSetChanged()
                        tvNoData.visibility = View.GONE
                    } else {
                        tvNoData.visibility = View.VISIBLE
                    }
                } else {
                    Toast.makeText(this@MaintenancePlanListActivity, "获取保养计划失败", Toast.LENGTH_SHORT).show()
                    tvNoData.visibility = View.VISIBLE
                }
            } catch (e: Exception) {
                Log.e(TAG, "加载保养计划异常", e)
                Toast.makeText(this@MaintenancePlanListActivity, "加载异常: ${e.message}", Toast.LENGTH_SHORT).show()
                tvNoData.visibility = View.VISIBLE
            }
        }
    }

    private fun showAddPlanDialog() {
        val dialog = Dialog(this)
        dialog.setContentView(R.layout.dialog_add_maintenance_plan)
        dialog.window?.setLayout(
            (resources.displayMetrics.widthPixels * 0.9).toInt(),
            ViewGroup.LayoutParams.WRAP_CONTENT
        )

        // 初始化对话框控件
        val spinnerDeviceId = dialog.findViewById<Spinner>(R.id.spinnerDeviceId)
        val etChecklist = dialog.findViewById<EditText>(R.id.etChecklist)
        val spinnerFrequency = dialog.findViewById<Spinner>(R.id.spinnerFrequency)
        val etStartTime = dialog.findViewById<EditText>(R.id.etStartTime)
        val etEndTime = dialog.findViewById<EditText>(R.id.etEndTime)
        val btnCancel = dialog.findViewById<Button>(R.id.btnCancel)
        val btnConfirm = dialog.findViewById<Button>(R.id.btnConfirm)

        // 设置设备下拉列表
        val deviceAdapter = ArrayAdapter(
            this,
            android.R.layout.simple_spinner_item,
            deviceList.map { it.deviceId }
        )
        deviceAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
        spinnerDeviceId.adapter = deviceAdapter

        // 设置频率下拉列表
        val frequencyAdapter = ArrayAdapter(
            this,
            android.R.layout.simple_spinner_item,
            arrayOf("月", "季度", "半年", "年")  // 移除"日"和"周"
        )
        frequencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
        spinnerFrequency.adapter = frequencyAdapter

        // 设置时间选择
        etStartTime.setOnClickListener {
            showDateTimePicker { dateTime ->
                etStartTime.setText(dateFormat.format(dateTime.time))
            }
        }

        etEndTime.setOnClickListener {
            showDateTimePicker { dateTime ->
                etEndTime.setText(dateFormat.format(dateTime.time))
            }
        }

        // 设置按钮点击事件
        btnCancel.setOnClickListener {
            dialog.dismiss()
        }

        btnConfirm.setOnClickListener {
            // 验证输入
            if (spinnerDeviceId.selectedItem == null) {
                Toast.makeText(this, "请选择设备", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            if (etChecklist.text.toString().trim().isEmpty()) {
                Toast.makeText(this, "请输入检查清单", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            if (etStartTime.text.toString().trim().isEmpty()) {
                Toast.makeText(this, "请选择开始时间", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            // 创建保养计划对象
            val isoDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
            isoDateFormat.timeZone = TimeZone.getTimeZone("UTC")
            
            val startTimeStr = etStartTime.text.toString().trim()
            val endTimeStr = etEndTime.text.toString().trim()
            
            val startTimeDate = dateFormat.parse(startTimeStr)
            val startTimeIso = if (startTimeDate != null) isoDateFormat.format(startTimeDate) else startTimeStr
            
            val endTimeIso = if (endTimeStr.isNotEmpty()) {
                val endTimeDate = dateFormat.parse(endTimeStr)
                if (endTimeDate != null) isoDateFormat.format(endTimeDate) else endTimeStr
            } else null

            val newPlan = MaintenancePlan(
                planId = 0,
                deviceId = spinnerDeviceId.selectedItem.toString(),
                checklist = etChecklist.text.toString().trim(),
                frequency = spinnerFrequency.selectedItem.toString(),
                status = "待启用",
                startTime = startTimeIso,
                endTime = endTimeIso
            )

            // 提交新计划
            addMaintenancePlan(newPlan)
            dialog.dismiss()
        }

        dialog.show()
    }

    private fun showDateTimePicker(callback: (Calendar) -> Unit) {
        val calendar = Calendar.getInstance()
        
        DatePickerDialog(
            this,
            { _, year, month, dayOfMonth ->
                calendar.set(Calendar.YEAR, year)
                calendar.set(Calendar.MONTH, month)
                calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
                
                TimePickerDialog(
                    this,
                    { _, hourOfDay, minute ->
                        calendar.set(Calendar.HOUR_OF_DAY, hourOfDay)
                        calendar.set(Calendar.MINUTE, minute)
                        calendar.set(Calendar.SECOND, 0)
                        callback(calendar)
                    },
                    calendar.get(Calendar.HOUR_OF_DAY),
                    calendar.get(Calendar.MINUTE),
                    true
                ).show()
            },
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH)
        ).show()
    }

    private fun addMaintenancePlan(plan: MaintenancePlan) {
        lifecycleScope.launch {
            try {
                Log.d(TAG, "发送的保养计划数据: $plan")
                
                val response = RetrofitClient.instance.addMaintenancePlan(plan)
                if (response.isSuccessful) {
                    Toast.makeText(this@MaintenancePlanListActivity, "添加保养计划成功", Toast.LENGTH_SHORT).show()
                    loadMaintenancePlans()
                } else {
                    val errorBody = response.errorBody()?.string() ?: "未知错误"
                    Log.e(TAG, "添加保养计划失败: $errorBody")
                    Toast.makeText(this@MaintenancePlanListActivity, "添加保养计划失败", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                Log.e(TAG, "添加保养计划异常", e)
                Toast.makeText(this@MaintenancePlanListActivity, "添加保养计划异常: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // 生成保养工单
    private fun generateMaintenanceOrder(planId: Int) {
        lifecycleScope.launch {
            try {
                val request = mapOf("planIds" to listOf(planId))
                val gson = Gson()
                val json = gson.toJson(request)
                val requestBody = json.toRequestBody("application/json".toMediaTypeOrNull())
                val response = RetrofitClient.instance.generateMaintenanceOrders(requestBody)

                if (response.isSuccessful) {
                    Toast.makeText(this@MaintenancePlanListActivity, "启用成功,已生成保养工单", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this@MaintenancePlanListActivity, "启用成功,但工单生成失败", Toast.LENGTH_SHORT).show()
                }

                // 重新加载保养计划列表
                loadMaintenancePlans()

            } catch (e: Exception) {
                Log.e(TAG, "生成工单异常", e)
                Toast.makeText(this@MaintenancePlanListActivity, "生成工单失败: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if (item.itemId == android.R.id.home) {
            finish()
            return true
        }
        return super.onOptionsItemSelected(item)
    }

    // 保养计划适配器
    inner class MaintenancePlanAdapter(private val plans: List<MaintenancePlan>) : 
            RecyclerView.Adapter<MaintenancePlanAdapter.ViewHolder>() {

        inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            val tvPlanId: TextView = itemView.findViewById(R.id.tvPlanId)
            val tvDeviceId: TextView = itemView.findViewById(R.id.tvDeviceId)
            val tvChecklist: TextView = itemView.findViewById(R.id.tvChecklist)
            val tvFrequency: TextView = itemView.findViewById(R.id.tvFrequency)
            val tvStatus: TextView = itemView.findViewById(R.id.tvStatus)
            val tvTimeRange: TextView = itemView.findViewById(R.id.tvTimeRange)
            val btnEnable: Button = itemView.findViewById(R.id.btnEnable)
            val btnDisable: Button = itemView.findViewById(R.id.btnDisable)
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_maintenance_plan, parent, false)
            return ViewHolder(view)
        }

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val plan = plans[position]
            holder.tvPlanId.text = "计划ID: ${plan.planId}"
            holder.tvDeviceId.text = "设备ID: ${plan.deviceId}"
            holder.tvChecklist.text = "检查清单: ${plan.checklist}"
            holder.tvFrequency.text = "保养频率: ${plan.frequency}"
            holder.tvStatus.text = "状态: ${plan.status}"
            
            // 根据状态设置文字颜色
            when (plan.status) {
                "启用" -> holder.tvStatus.setTextColor(0xFF4CAF50.toInt())
                "停用" -> holder.tvStatus.setTextColor(0xFFF44336.toInt())
                "待启用" -> holder.tvStatus.setTextColor(0xFFFF9800.toInt())
            }
            
            val timeRange = if (plan.endTime != null) {
                "${plan.startTime} ~ ${plan.endTime}"
            } else {
                "${plan.startTime} 起"
            }
            holder.tvTimeRange.text = "时间范围: $timeRange"
            
            // 根据当前状态设置按钮可用性
            if (plan.status == "启用") {
                holder.btnEnable.isEnabled = false
                holder.btnDisable.isEnabled = true
            } else {
                holder.btnEnable.isEnabled = true
                holder.btnDisable.isEnabled = false
            }
            
            // 设置启用按钮点击事件
            holder.btnEnable.setOnClickListener {
                updatePlanStatus(plan.planId, "启用")
            }
            
            // 设置停用按钮点击事件
            holder.btnDisable.setOnClickListener {
                updatePlanStatus(plan.planId, "停用")
            }
            
            // 设置整个项目的点击事件,打开编辑对话框
            holder.itemView.setOnClickListener {
                showEditPlanDialog(plan)
            }
        }

        override fun getItemCount() = plans.size
    }
    
    // 更新保养计划状态
    private fun updatePlanStatus(planId: Int, status: String) {
        lifecycleScope.launch {
            try {
                val response = RetrofitClient.instance.updateMaintenancePlanStatus(planId, status)
                if (response.isSuccessful) {
                    Toast.makeText(this@MaintenancePlanListActivity, "更新状态成功", Toast.LENGTH_SHORT).show()
                    
                    // 如果是启用操作,则生成对应的保养工单
                    if (status == "启用") {
                        generateMaintenanceOrder(planId)
                    } else {
                        loadMaintenancePlans()
                    }
                } else {
                    Toast.makeText(this@MaintenancePlanListActivity, "更新状态失败", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                Log.e(TAG, "更新状态异常", e)
                Toast.makeText(this@MaintenancePlanListActivity, "更新状态异常: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }
    
    // 显示编辑保养计划对话框
    private fun showEditPlanDialog(plan: MaintenancePlan) {
        // 编辑对话框实现(类似于添加对话框,但预填充数据)
        // 这里可以复用showAddPlanDialog的逻辑,传入plan参数进行数据预填充
    }
}

package com.example.sanpaias.activity

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.util.Base64
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.sanpaias.R
import com.example.sanpaias.adapter.PhotoAdapter
import com.example.sanpaias.entity.MaintenanceOrder
import com.example.sanpaias.network.RetrofitClient
import kotlinx.coroutines.launch
import java.io.ByteArrayOutputStream
import java.io.File
import java.text.SimpleDateFormat
import java.util.*

// 位置相关导入
import android.location.Location
import android.location.Geocoder
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import java.util.Locale

class MaintenanceTaskEditActivity : AppCompatActivity() {

    companion object {
        private const val CAMERA_PERMISSION_REQUEST_CODE = 100
        private const val REQUEST_LOCATION_PERMISSION = 102
    }

    private lateinit var tvOrderId: TextView
    private lateinit var tvPlanId: TextView
    private lateinit var tvStatus: TextView
    private lateinit var etMaintenanceDesc: EditText
    private lateinit var btnTakePhoto: Button
    private lateinit var btnSave: Button
    private lateinit var btnSubmit: Button
    private lateinit var rvPhotos: RecyclerView
    private lateinit var photoAdapter: PhotoAdapter
    private lateinit var maintenanceOrder: MaintenanceOrder

    // 位置相关变量
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private var currentLocation: Location? = null

    private val beforePhotoList = mutableListOf<String>()
    private val afterPhotoList = mutableListOf<String>()
    private val allPhotoList = mutableListOf<String>() // 用于显示的合并列表
    private var isCapturingBeforePhoto = true
    
    private var currentPhotoFile: File? = null
    
    private val cameraLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == RESULT_OK) {
            currentPhotoFile?.let { file ->
                if (file.exists()) {
                    val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, Uri.fromFile(file))
                    val timestampedBitmap = addTimestampToBitmap(bitmap)
                    val base64Image = bitmapToBase64(timestampedBitmap)
                    
                    if (isCapturingBeforePhoto) {
                        beforePhotoList.add(base64Image)
                    } else {
                        afterPhotoList.add(base64Image)
                    }
                    allPhotoList.add(base64Image)
                    photoAdapter.notifyDataSetChanged()
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_task_edit)

        // 初始化位置服务
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
        
        // 获取当前位置
        getCurrentLocation()

        // 从全局管理器获取数据
        maintenanceOrder = DataManager.getCurrentOrder() ?: return
        
        initViews()
        setupRecyclerView()
        displayOrderInfo()
        setupButtonListeners()
    }

    override fun onDestroy() {
        super.onDestroy()
        // 清理全局数据
        DataManager.clearCurrentOrder()
    }

    private fun initViews() {
        tvOrderId = findViewById(R.id.tvOrderId)
        tvPlanId = findViewById(R.id.tvFaultId)
        tvStatus = findViewById(R.id.tvStatus)
        etMaintenanceDesc = findViewById(R.id.etRepairDesc)
        btnTakePhoto = findViewById(R.id.btnTakePhoto)
        btnSave = findViewById(R.id.btnSave)
        btnSubmit = findViewById(R.id.btnSubmit)
        rvPhotos = findViewById(R.id.recyclerViewPhotos)
    }

    private fun setupRecyclerView() {
        photoAdapter = PhotoAdapter(allPhotoList) { position ->
            // 删除照片时需要判断是保养前还是保养后照片
            val photo = allPhotoList[position]
            if (beforePhotoList.contains(photo)) {
                beforePhotoList.remove(photo)
            } else {
                afterPhotoList.remove(photo)
            }
            allPhotoList.removeAt(position)
            photoAdapter.notifyItemRemoved(position)
        }
        rvPhotos.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
        rvPhotos.adapter = photoAdapter
    }

    private fun displayOrderInfo() {
        tvOrderId.text = "工单编号: ${maintenanceOrder.orderId}"
        tvPlanId.text = "计划编号: ${maintenanceOrder.planId}"
        tvStatus.text = "状态: ${maintenanceOrder.status}"
        etMaintenanceDesc.setText(maintenanceOrder.maintenanceDesc ?: "")

        // 加载现有保养前照片
        maintenanceOrder.beforePhotos?.let { photos ->
            if (photos.isNotEmpty()) {
                val photosList = photos.split(",")
                beforePhotoList.addAll(photosList)
                allPhotoList.addAll(photosList)
            }
        }
        
        // 加载现有保养后照片
        maintenanceOrder.afterPhotos?.let { photos ->
            if (photos.isNotEmpty()) {
                val photosList = photos.split(",")
                afterPhotoList.addAll(photosList)
                allPhotoList.addAll(photosList)
            }
        }
        
        photoAdapter.notifyDataSetChanged()
    }

    private fun setupButtonListeners() {
        btnTakePhoto.setOnClickListener {
            showPhotoTypeDialog()
        }

        btnSave.setOnClickListener {
            saveMaintenanceOrder()
        }

        btnSubmit.setOnClickListener {
            submitMaintenanceOrder()
        }
    }

    private fun showPhotoTypeDialog() {
        val options = arrayOf("拍摄保养前照片", "拍摄保养后照片")
        AlertDialog.Builder(this)
            .setTitle("选择照片类型")
            .setItems(options) { _, which ->
                isCapturingBeforePhoto = (which == 0)
                if (checkCameraPermission()) {
                    dispatchTakePictureIntent()
                } else {
                    requestCameraPermission()
                }
            }
            .show()
    }

    private fun checkCameraPermission(): Boolean {
        val cameraPermission = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.CAMERA
        ) == PackageManager.PERMISSION_GRANTED
        
        val locationPermission = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
        
        return cameraPermission && locationPermission
    }

    private fun requestCameraPermission() {
        val permissionsNeeded = mutableListOf<String>()
        
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            permissionsNeeded.add(Manifest.permission.CAMERA)
        }
        
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION)
        }
        
        if (permissionsNeeded.isNotEmpty()) {
            ActivityCompat.requestPermissions(this, permissionsNeeded.toTypedArray(), CAMERA_PERMISSION_REQUEST_CODE)
        }
    }

    private fun dispatchTakePictureIntent() {
        Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
            takePictureIntent.resolveActivity(packageManager)?.also {
                currentPhotoFile = createImageFile()
                currentPhotoFile?.also {
                    val photoURI: Uri = FileProvider.getUriForFile(
                        this,
                        "com.example.sanpaias.fileprovider",
                        it
                    )
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                    cameraLauncher.launch(takePictureIntent)
                }
            }
        }
    }

    private fun createImageFile(): File {
        val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
        val storageDir: File = getExternalFilesDir("Pictures")!!
        return File.createTempFile(
            "JPEG_${timeStamp}_",
            ".jpg",
            storageDir
        )
    }

    private fun getCurrentLocation() {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            fusedLocationClient.lastLocation
                .addOnSuccessListener { location: Location? ->
                    currentLocation = location
                }
        }
    }

    private fun getLocationText(): String {
        return currentLocation?.let { location ->
            try {
                val geocoder = Geocoder(this, Locale.getDefault())
                val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1)
                if (addresses?.isNotEmpty() == true) {
                    val address = addresses[0]
                    val addressText = StringBuilder()
                    
                    // 构建详细地址
                    address.adminArea?.let { addressText.append(it) }  // 省份
                    address.locality?.let { 
                        if (addressText.isNotEmpty()) addressText.append(" ")
                        addressText.append(it) 
                    }  // 城市
                    address.subLocality?.let { 
                        if (addressText.isNotEmpty()) addressText.append(" ")
                        addressText.append(it) 
                    }  // 区县
                    
                    if (addressText.isNotEmpty()) {
                        "位置: $addressText"
                    } else {
                        "位置: ${String.format("%.6f", location.latitude)}, ${String.format("%.6f", location.longitude)}"
                    }
                } else {
                    "位置: ${String.format("%.6f", location.latitude)}, ${String.format("%.6f", location.longitude)}"
                }
            } catch (e: Exception) {
                "位置: ${String.format("%.6f", location.latitude)}, ${String.format("%.6f", location.longitude)}"
            }
        } ?: ""
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            CAMERA_PERMISSION_REQUEST_CODE -> {
                var cameraGranted = false
                var locationGranted = false
                
                for (i in permissions.indices) {
                    when (permissions[i]) {
                        Manifest.permission.CAMERA -> {
                            cameraGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED
                        }
                        Manifest.permission.ACCESS_FINE_LOCATION -> {
                            locationGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED
                            if (locationGranted) {
                                getCurrentLocation()
                            }
                        }
                    }
                }
                
                if (cameraGranted) {
                    showPhotoTypeDialog()
                } else {
                    Toast.makeText(this, "需要相机权限才能拍照", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    // 修改水印方法,采用维修工单的样式
    private fun addTimestampToBitmap(bitmap: Bitmap): Bitmap {
        val width = bitmap.width
        val height = bitmap.height
        
        val config = bitmap.config ?: Bitmap.Config.ARGB_8888
        val result = Bitmap.createBitmap(width, height, config)
        
        val canvas = Canvas(result)
        canvas.drawBitmap(bitmap, 0f, 0f, null)
        
        // 设置保养时间水印
        val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
        val locationText = getLocationText()
        
        val paint = Paint().apply {
            color = Color.WHITE
            textSize = width / 35f  // 与维修工单保持一致
            isAntiAlias = true
            style = Paint.Style.FILL
            setShadowLayer(3f, 1f, 1f, Color.BLACK)  // 添加阴影
        }
        
        // 计算时间文本位置(右下角)
        val timeBounds = Rect()
        paint.getTextBounds(timestamp, 0, timestamp.length, timeBounds)
        val timeTextWidth = paint.measureText(timestamp)
        val timeTextHeight = timeBounds.height()
        
        val timeX = width - timeTextWidth - width / 50f
        val timeY = height - timeTextHeight - height / 50f
        
        // 绘制时间文本
        canvas.drawText(timestamp, timeX, timeY, paint)
        
        // 如果有位置信息,绘制位置文本(时间上方)
        if (locationText.isNotEmpty()) {
            val locationBounds = Rect()
            paint.getTextBounds(locationText, 0, locationText.length, locationBounds)
            val locationTextWidth = paint.measureText(locationText)
            val locationTextHeight = locationBounds.height()
            
            val locationX = width - locationTextWidth - width / 50f
            val locationY = timeY - locationTextHeight - height / 100f  // 在时间上方,留出间距
            
            // 绘制位置文本
            canvas.drawText(locationText, locationX, locationY, paint)
        }
        
        return result
    }

    private fun bitmapToBase64(bitmap: Bitmap): String {
        val byteArrayOutputStream = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream)
        val byteArray = byteArrayOutputStream.toByteArray()
        return Base64.encodeToString(byteArray, Base64.DEFAULT)
    }

    private fun saveMaintenanceOrder() {
        val updatedOrder = maintenanceOrder.copy(
            maintenanceDesc = etMaintenanceDesc.text.toString(),
            beforePhotos = if (beforePhotoList.isNotEmpty()) beforePhotoList.joinToString(",") else null,
            afterPhotos = if (afterPhotoList.isNotEmpty()) afterPhotoList.joinToString(",") else null
        )

        lifecycleScope.launch {
            try {
                val response = RetrofitClient.instance.updateMaintenanceOrder(updatedOrder)
                if (response.isSuccessful) {
                    Toast.makeText(this@MaintenanceTaskEditActivity, "保存成功", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this@MaintenanceTaskEditActivity, "保存失败: ${response.message()}", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                Log.e("MaintenanceTaskEdit", "保存失败", e)
                Toast.makeText(this@MaintenanceTaskEditActivity, "保存失败: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun submitMaintenanceOrder() {
        val updatedOrder = maintenanceOrder.copy(
            maintenanceDesc = etMaintenanceDesc.text.toString(),
            beforePhotos = if (beforePhotoList.isNotEmpty()) beforePhotoList.joinToString(",") else null,
            afterPhotos = if (afterPhotoList.isNotEmpty()) afterPhotoList.joinToString(",") else null,
            status = "待审批"
        )

        lifecycleScope.launch {
            try {
                val response = RetrofitClient.instance.updateMaintenanceOrder(updatedOrder)
                if (response.isSuccessful) {
                    Toast.makeText(this@MaintenanceTaskEditActivity, "提交成功", Toast.LENGTH_SHORT).show()
                    finish()
                } else {
                    Toast.makeText(this@MaintenanceTaskEditActivity, "提交失败: ${response.message()}", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                Log.e("MaintenanceTaskEdit", "提交失败", e)
                Toast.makeText(this@MaintenanceTaskEditActivity, "提交失败: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }
}
package com.example.sanpaias.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import com.example.sanpaias.R
import com.example.sanpaias.entity.MaintenanceOrder
import android.content.Intent
import android.widget.Button
import com.example.sanpaias.activity.MaintenanceTaskEditActivity

class MaintenanceTaskAdapter(
    private val onTakeTask: (MaintenanceOrder) -> Unit
) : RecyclerView.Adapter<MaintenanceTaskAdapter.TaskViewHolder>() {

    private var tasks: List<MaintenanceOrder> = emptyList()

    class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val cardView: CardView = itemView.findViewById(R.id.cardView)
        val tvOrderId: TextView = itemView.findViewById(R.id.tvOrderId)
        val tvFaultId: TextView = itemView.findViewById(R.id.tvFaultId)
        val tvStatus: TextView = itemView.findViewById(R.id.tvStatus)
        val tvRepairDesc: TextView = itemView.findViewById(R.id.tvRepairDesc)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_my_task, parent, false)
        return TaskViewHolder(view)
    }

    override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
        val task = tasks[position]
        
        holder.tvOrderId.text = "工单编号: ${task.orderId}"
        holder.tvFaultId.text = "计划编号: ${task.planId}"
        holder.tvStatus.text = "状态: ${task.status ?: "待处理"}"
        holder.tvRepairDesc.text = "保养描述: ${task.maintenanceDesc ?: "暂无描述"}"
        
        holder.cardView.setOnClickListener {
            val context = holder.itemView.context
            
            // 将数据存储到全局管理器
            DataManager.setCurrentOrder(task)
            
            val intent = Intent(context, MaintenanceTaskEditActivity::class.java)
            context.startActivity(intent)
        }
    }

    override fun getItemCount() = tasks.size

    fun updateData(newTasks: List<MaintenanceOrder>) {
        tasks = newTasks
        notifyDataSetChanged()
    }
}
package com.example.sanpaias.entity

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class MaintenanceOrder(
    val orderId: String,
    val planId: Int,
    val engineerId: String?,
    val planTime: String?,
    val maintenanceDesc: String?,
    val status: String,
    val beforePhotos: String?,
    val afterPhotos: String?
) : Parcelable

<?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="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".activity.MaintenancePlanListActivity">

    <Button
        android:id="@+id/btnAddPlan"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:text="新增保养计划"
        android:textSize="16sp" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvMaintenancePlans"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <TextView
        android:id="@+id/tvNoData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="暂无保养计划数据"
        android:textSize="16sp"
        android:visibility="gone" />

</LinearLayout>
posted @ 2025-05-21 19:46  花落水无痕  阅读(5)  评论(0)    收藏  举报