<template>
    <div class="water-page device-page">
        <div class="page-heading">
            <div class="page-title">
                <span class="page-title-icon"><el-icon><component :is="pageIcon"/></el-icon></span>
                <div><h2>{{ title }}</h2><span class="muted">{{ subtitle }}</span></div>
            </div>
            <el-button class="heading-refresh" :icon="Refresh" :loading="loading" @click="loadData">刷新</el-button>
        </div>

        <div v-if="view === 'abnormal'" class="metric-grid">
            <div v-for="item in abnormalStats" :key="item.label" class="metric">
                <div class="metric-icon">
                    <el-icon>
                        <component :is="item.icon"/>
                    </el-icon>
                </div>
                <div class="metric-body">
                    <span>{{ item.label }}</span>
                    <div class="metric-value"><strong>{{ item.value }}</strong><small>{{ item.unit }}</small></div>
                </div>
            </div>
        </div>

        <div v-if="view === 'ledger'" class="metric-grid">
            <div v-for="item in ledgerStatsCards" :key="item.label" class="metric">
                <div class="metric-icon">
                    <el-icon>
                        <component :is="item.icon"/>
                    </el-icon>
                </div>
                <div class="metric-body">
                    <span>{{ item.label }}</span>
                    <div class="metric-value"><strong>{{ item.value }}</strong><small>{{ item.unit }}</small></div>
                </div>
            </div>
        </div>

        <el-card shadow="never" class="filter-card">
            <el-form :inline="true" :model="query">
                <el-form-item v-if="view !== 'gis'" label="设备名称">
                    <el-input v-model="query.equipmentName" clearable/>
                </el-form-item>
                <el-form-item v-if="view !== 'gis'" label="设备编码">
                    <el-input v-model="query.equipmentCode" clearable/>
                </el-form-item>
                <el-form-item v-if="view === 'ledger'" label="报警状态">
                    <el-select v-model="query.alarmStatus" clearable class="filter-select" style="width:180px">
                        <el-option label="正常" :value="0"/>
                        <el-option label="报警" :value="1"/>
                    </el-select>
                </el-form-item>
                <el-form-item v-if="view === 'ledger' || view === 'dispatch' || view === 'abnormal'" label="报警等级">
                    <el-select v-model="query.alertLevel" clearable class="filter-select" style="width:180px">
                        <el-option v-for="item in levels" :key="item.value" v-bind="item"/>
                    </el-select>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="loadData">查询</el-button>
                    <el-button @click="reset">重置</el-button>
                </el-form-item>
            </el-form>
        </el-card>

        <el-card shadow="never" class="water-panel">
            <div class="card-title"><span><el-icon><Monitor/></el-icon> {{ title }}清单</span>
                <div class="panel-actions">
                    <el-button v-if="view === 'ledger'" type="primary" plain size="small" :icon="Plus"
                               @click="openCreate">新增
                    </el-button>
                    <el-button v-if="view === 'ledger'" type="danger" plain size="small" :icon="Delete"
                               :disabled="!selectedDevices.length" @click="removeSelectedDevices">删除
                    </el-button>
                    <el-button v-if="view === 'ledger'" type="info" plain size="small" :icon="Upload"
                               @click="openImport">导入
                    </el-button>
                    <el-button v-if="view !== 'gis'" type="success" plain size="small" :icon="Download"
                               @click="exportData">导出
                    </el-button>
                </div>
            </div>
            <el-alert v-if="loadError" :title="loadError" type="error" show-icon :closable="false" class="load-error">
                <el-button size="small" type="danger" plain @click="loadData">重试</el-button>
            </el-alert>
            <el-empty v-if="view === 'gis' && !loading && !loadError && !page.records.length"
                      description="暂无设备 GIS 点位数据"/>
            <el-table :data="page.records" border stripe v-loading="loading" row-key="equipmentId"
                      @selection-change="handleDeviceSelectionChange">
                <el-table-column v-if="view === 'ledger'" type="selection" width="48" align="center"/>
                <el-table-column type="index" label="#" width="55"/>
                <el-table-column prop="equipmentCode" label="设备编码" min-width="130"/>
                <el-table-column prop="equipmentName" label="设备名称" min-width="150"/>
                <el-table-column prop="equipmentTypeName" label="设备类型" min-width="120"/>
                <el-table-column prop="equipmentLocation" label="安装位置" min-width="180" show-overflow-tooltip/>
                <el-table-column v-if="view === 'ledger'" label="设备图片" width="110" align="center">
                    <template #default="{ row }">
                        <el-image v-if="row.equipmentImageUrl" :src="row.equipmentImageUrl"
                                  :preview-src-list="[row.equipmentImageUrl]" preview-teleported hide-on-click-modal
                                  fit="cover" class="device-image"/>
                        <span v-else class="image-placeholder">暂无图片</span></template>
                </el-table-column>
                <el-table-column v-if="view !== 'maintain'" label="设备状态" width="100">
                    <template #default="{ row }">
                        <el-tag :type="Number(row.currentStatus) === 3 ? 'danger' : 'success'">{{ row.currentStatusText
                            }}
                        </el-tag>
                    </template>
                </el-table-column>
                <el-table-column v-if="view !== 'maintain'" label="在线状态" width="100">
                    <template #default="{ row }">
                        <el-tag :type="Number(row.onlineStatus) === 1 ? 'success' : 'info'">{{ row.onlineStatusText }}
                        </el-tag>
                    </template>
                </el-table-column>
                <el-table-column v-if="view !== 'maintain'" label="报警状态" width="100">
                    <template #default="{ row }">
                        <el-tag :type="Number(row.alarmStatus) === 1 ? 'danger' : 'success'">{{ row.alarmStatusText }}
                        </el-tag>
                    </template>
                </el-table-column>
                <el-table-column v-if="view !== 'maintain'" label="报警等级" width="110">
                    <template #default="{ row }">
                        <el-tag :type="alarmLevelType(row.alertLevel)">{{ row.alarmLevelText }}</el-tag>
                    </template>
                </el-table-column>
                <el-table-column v-if="view === 'abnormal' || view === 'dispatch'" label="异常原因" min-width="150"
                                 show-overflow-tooltip>
                    <template #default="{ row }">{{ row.abnormalReasonText || '-' }}</template>
                </el-table-column>
                <el-table-column label="关联工单" min-width="190" show-overflow-tooltip>
                    <template #default="{ row }">{{ view === 'maintain' ? (row.orderNo || '-') : row.relatedOrderNo }}
                    </template>
                </el-table-column>
                <el-table-column label="工单状态" min-width="110">
                    <template #default="{ row }">
                        <el-tag v-if="view !== 'maintain' && row.workOrderStatusText !== '暂无工单'"
                                :type="workOrderStatusType(row.workOrderStatusCode)">{{ row.workOrderStatusText }}
                        </el-tag>
                        <span v-else>{{ view === 'maintain' ? (row.status || '暂无') : row.workOrderStatusText }}</span>
                    </template>
                </el-table-column>
                <el-table-column v-if="view === 'abnormal' || view === 'dispatch'" label="接单责任人" min-width="110">
                    <template #default="{ row }">{{ view === 'maintain' ? (row.maintainPerson || '-') :
                        (row.relatedWorkOrderPerson || '-') }}
                    </template>
                </el-table-column>
                <el-table-column label="操作" width="210" fixed="right">
                    <template #default="{ row }">
                        <div class="facility-row-actions">
                            <el-tooltip content="查看详情" placement="top">
                                <el-button class="facility-row-action is-detail" aria-label="查看详情" size="small"
                                           type="primary" plain circle :icon="View" @click="selectPoint(row)"/>
                            </el-tooltip>
                            <el-tooltip v-if="view === 'ledger'" content="修改信息" placement="top">
                                <el-button class="facility-row-action is-edit" aria-label="修改信息" size="small"
                                           type="warning" plain circle :icon="Edit" @click="openEdit(row)"/>
                            </el-tooltip>
                            <el-tooltip v-if="canDispatch(row)" content="维修派单" placement="top">
                                <el-button class="facility-row-action is-dispatch" aria-label="维修派单" size="small"
                                           type="warning" plain circle :icon="Position" @click="openOrder(row)"/>
                            </el-tooltip>
                            <el-tooltip v-if="view === 'ledger'" content="删除设备" placement="top">
                                <el-button class="facility-row-action is-delete" aria-label="删除设备" size="small"
                                           type="danger" plain circle :icon="Delete" @click="removeDevice(row)"/>
                            </el-tooltip>
                            <el-tooltip v-if="view === 'maintain'" content="删除记录" placement="top">
                                <el-button class="facility-row-action is-delete" aria-label="删除记录" size="small"
                                           type="danger" plain circle :icon="Delete" @click="removeRecord(row)"/>
                            </el-tooltip>
                        </div>
                    </template>
                </el-table-column>
            </el-table>
            <el-pagination v-model:current-page="query.pageNum" v-model:page-size="query.pageSize" :total="page.total"
                           layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]" @change="loadData"/>
        </el-card>

        <el-drawer v-model="detailVisible" class="detail-drawer device-detail-drawer" title="设备详情" direction="rtl"
                   size="680px" append-to-body>
            <div class="detail-summary"><span class="detail-name">{{ detail.equipmentName || '-' }}</span>
                <el-tag :type="Number(detail.currentStatus) === 3 ? 'danger' : 'success'">{{ detail.currentStatusText ||
                    '-' }}
                </el-tag>
                <el-tag :type="Number(detail.alarmStatus) === 1 ? 'danger' : 'success'">{{ detail.alarmStatusText || '-'
                    }}
                </el-tag>
                <el-tag v-if="detail.workOrderStatusText && detail.workOrderStatusText !== '暂无工单'"
                        :type="workOrderStatusType(detail.workOrderStatusCode)">{{ detail.workOrderStatusText }}
                </el-tag>
            </div>
            <div v-if="view === 'ledger'" class="detail-media">
                <div class="detail-media-label">设备图片</div>
                <el-image v-if="detail.equipmentImageUrl" :src="detail.equipmentImageUrl"
                          :preview-src-list="[detail.equipmentImageUrl]" preview-teleported hide-on-click-modal
                          fit="cover"
                          class="detail-image"/>
                <div v-else class="detail-image-placeholder">暂无图片</div>
            </div>
            <el-descriptions :column="2" border>
                <el-descriptions-item v-for="item in detailFields" :key="item.label" :label="item.label">{{ item.value
                    }}
                </el-descriptions-item>
            </el-descriptions>
            <template #footer>
                <el-button @click="detailVisible = false">关闭</el-button>
            </template>
        </el-drawer>
        <el-drawer v-model="editVisible" class="detail-drawer device-edit-drawer"
                   :title="editMode === 'create' ? '新增设备信息' : '修改设备信息'" direction="rtl" size="680px"
                   append-to-body>
            <el-form ref="editFormRef" :model="editForm" label-width="110px" :rules="editRules">
                <el-row :gutter="16">
                    <el-col :span="12">
                        <el-form-item label="设备编码" prop="equipmentCode">
                            <el-input v-model="editForm.equipmentCode" :disabled="editMode === 'edit'" maxlength="50"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="设备类型" prop="equipmentTypeId">
                            <el-select v-if="editMode === 'create'" v-model="editForm.equipmentTypeId" filterable
                                       class="full-input">
                                <el-option v-for="item in waterDeviceTypes" :key="item.id" :label="item.typeName"
                                           :value="item.id"/>
                            </el-select>
                            <el-input v-else v-model="editForm.equipmentTypeName" disabled/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="设备名称" prop="equipmentName">
                            <el-input v-model="editForm.equipmentName" clearable/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="设备型号">
                            <el-input v-model="editForm.equipmentModel" clearable/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="所属片区">
                            <el-select v-model="editForm.district" clearable class="full-input">
                                <el-option label="城南" value="城南"/>
                                <el-option label="城北" value="城北"/>
                                <el-option label="太常" value="太常"/>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="制造商">
                            <el-input v-model="editForm.manufacturer" clearable/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="经度">
                            <el-input-number v-model="editForm.longitude" :precision="8" :controls="false"
                                             class="full-input"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="纬度">
                            <el-input-number v-model="editForm.latitude" :precision="8" :controls="false"
                                             class="full-input"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="24">
                        <el-form-item label="安装位置">
                            <el-input v-model="editForm.equipmentLocation" clearable/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="设备负责人">
                            <el-input v-model="editForm.maintainer" clearable/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="联系方式">
                            <el-input v-model="editForm.maintainerPhone" clearable/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="24">
                        <el-form-item label="权属单位">
                            <el-input v-model="editForm.ownershipUnit" clearable/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="24">
                        <el-form-item label="设备图片">
                            <ImageUpload v-model="editForm.equipmentImage" :limit="1" :file-size="5"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="24">
                        <el-form-item label="备注">
                            <el-input v-model="editForm.remark" type="textarea" :rows="3" maxlength="500"
                                      show-word-limit/>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <el-button @click="editVisible = false">取消</el-button>
                <el-button type="primary" :loading="editSaving" @click="submitEdit">保存</el-button>
            </template>
        </el-drawer>
        <el-dialog v-model="dispatchVisible" class="dispatch-dialog" title="设备维修派单" width="860px">
            <div class="dispatch-layout">
                <div class="assignee-picker">
                    <aside class="assignee-depts">
                        <el-input v-model="deptFilterText" clearable placeholder="筛选部门" size="small"/>
                        <el-tree ref="deptTreeRef" :data="deptTreeData" node-key="deptId" highlight-current
                                 :props="{ label: 'deptName', children: 'children' }"
                                 :filter-node-method="filterDeptNode" class="assignee-tree"
                                 @node-click="handleDeptClick"/>
                    </aside>
                    <section class="assignee-users">
                        <div class="assignee-search">
                            <el-input v-model="assigneeKeyword" clearable placeholder="搜索用户名、昵称或手机号"
                                      @keyup.enter="searchAssigneeUsers"/>
                            <el-button type="primary" :loading="userLoading" @click="searchAssigneeUsers">查询
                            </el-button>
                        </div>
                        <el-alert v-if="userLoadError" :title="userLoadError" type="error" show-icon :closable="false">
                            <el-button size="small" type="danger" plain @click="retryAssigneeLoading">重试</el-button>
                        </el-alert>
                        <el-table :data="users" border row-key="userId" highlight-current-row
                                  :current-row-key="assigneeDraft?.userId" @row-click="chooseAssignee">
                            <el-table-column label="选择" width="58" align="center">
                                <template #default="{ row }">
                                    <el-radio :model-value="assigneeDraft?.userId" :label="row.userId"
                                              @click.stop="chooseAssignee(row)">&nbsp;
                                    </el-radio>
                                </template>
                            </el-table-column>
                            <el-table-column prop="nickName" label="昵称" min-width="100"/>
                            <el-table-column prop="userName" label="用户名" min-width="110"/>
                            <el-table-column prop="deptName" label="部门" min-width="120" show-overflow-tooltip/>
                            <el-table-column prop="phone" label="手机号" min-width="130"/>
                            <el-table-column prop="statusText" label="账号状态" width="90"/>
                        </el-table>
                        <el-empty v-if="!userLoading && !userLoadError && !users.length"
                                  description="暂无符合条件的责任人" :image-size="80"/>
                        <el-pagination v-model:current-page="assigneePageNum" v-model:page-size="assigneePageSize"
                                       :total="assigneeTotal" layout="total, sizes, prev, pager, next"
                                       :page-sizes="[10, 20, 50]" @current-change="loadAssigneeUsers"
                                       @size-change="handleAssigneeSizeChange"/>
                        <div class="assignee-picked">已选责任人：<strong v-if="assigneeDraft">{{ assigneeDraft.nickName
                            || assigneeDraft.userName }}</strong><span v-else>未选择</span>
                            <el-button size="small" type="primary" plain :disabled="!assigneeDraft"
                                       @click="confirmAssignee">确认选择
                            </el-button>
                        </div>
                    </section>
                </div>
                <el-form class="dispatch-form" :model="dispatchForm" label-width="100px">
                    <el-form-item label="设备"><span>{{ dispatchDevice.equipmentName }}（{{ dispatchDevice.equipmentCode }}）</span>
                    </el-form-item>
                    <el-form-item label="工单类型"><span>1-故障维修</span></el-form-item>
                    <el-form-item label="优先级" required>
                        <el-select v-model="dispatchForm.orderLevel" placeholder="请选择优先级">
                            <el-option label="紧急" :value="1"/>
                            <el-option label="一般" :value="2"/>
                            <el-option label="低" :value="3"/>
                        </el-select>
                    </el-form-item>
                    <el-form-item label="问题描述" required>
                        <el-input v-model="dispatchForm.description" type="textarea" :rows="5" maxlength="500"
                                  show-word-limit placeholder="请输入故障或异常描述"/>
                    </el-form-item>
                    <el-form-item label="接单责任人" required>
                        <span v-if="selectedAssignee" class="assignee-summary">{{ selectedAssignee.nickName || selectedAssignee.userName }}（{{ selectedAssignee.deptName || '-' }}）</span>
                        <span v-else class="assignee-placeholder">请在左侧选择责任人</span>
                    </el-form-item>
                </el-form>
            </div>
            <template #footer>
                <el-button @click="dispatchVisible = false">取消</el-button>
                <el-button type="primary" :loading="dispatching" @click="submitDispatch">确认派单</el-button>
            </template>
        </el-dialog>

        <el-dialog v-model="importVisible" title="供水设备批量导入" width="520px" append-to-body @closed="resetImport">
            <el-upload ref="importUploadRef" drag :limit="1" accept=".xlsx,.xls" :auto-upload="false"
                       :file-list="importFileList" :on-change="handleImportChange" :on-remove="handleImportRemove"
                       :disabled="importUploading">
                <el-icon class="el-icon--upload">
                    <Upload/>
                </el-icon>
                <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
                <template #tip>
                    <div class="el-upload__tip import-tip">仅支持 xls、xlsx；设备类型必须属于供水类型树。</div>
                </template>
            </el-upload>
            <el-checkbox v-model="importUpdateSupport" class="import-update">更新已存在的设备编码</el-checkbox>
            <template #footer>
                <el-button type="primary" :loading="importUploading" @click="submitImport">开始导入</el-button>
                <el-button @click="importVisible = false">取消</el-button>
            </template>
        </el-dialog>
    </div>
</template>

<script setup>
    import {computed, onMounted, ref, watch} from 'vue'
    import {ElMessage, ElMessageBox} from 'element-plus'
    import {
        Monitor,
        Refresh,
        Warning,
        Bell,
        Tools,
        DataLine,
        Upload,
        Download,
        Search,
        Plus,
        Delete,
        View,
        Edit,
        Position
    } from '@element-plus/icons-vue'
    import {listUser} from '@/api/system/user'
    import {listDept} from '@/api/system/dept'
    import {handleTree} from '@/utils/ruoyi'
    import {
        createAbnormalOrder,
        createWaterDevice,
        deleteMaintainRecord,
        deleteWaterDevices,
        exportAbnormalDevices,
        exportDeviceLedger,
        getDeviceLedgerStatistics,
        getWaterDeviceById,
        importDeviceLedger,
        listAbnormalDevices,
        listDeviceGisPoints,
        listDeviceLedger,
        listMaintainRecords,
        updateWaterDevice
    } from '@/api/pipeNetwork/waterSupply'
    import {listEquipmentTypeAll} from '@/api/pipeNetwork/pdeviceInform'
    import {normalizePage, readMonitoringExportError} from '@/utils/waterSupplyModel'
    import {
        buildAssigneeQuery,
        buildMaintainOrderPayload,
        canDispatchDevice,
        normalizeAssigneeUser,
        normalizeDeviceGisPoints,
        normalizeDeviceRow,
        normalizeDispatchOrderResponse,
        preserveDevicePageOnError,
        selectSingleAssignee
    } from '@/utils/waterSupplyUiModel'
    import {saveAs} from 'file-saver'

    const props = defineProps({
        title: {type: String, required: true},
        subtitle: {type: String, default: '设备信息、运行状态与维护过程管理'},
        view: {type: String, default: 'ledger'}
    })
    const {title, subtitle, view} = props
    const query = ref({
        pageNum: 1,
        pageSize: 10,
        equipmentName: '',
        equipmentCode: '',
        alarmStatus: undefined,
        alertLevel: undefined
    })
    const importVisible = ref(false);
    const importUploading = ref(false);
    const importUpdateSupport = ref(false);
    const importFileList = ref([]);
    const importUploadRef = ref(null);
    const editVisible = ref(false);
    const editSaving = ref(false);
    const editFormRef = ref(null);
    const editForm = ref({})
    const editMode = ref('edit');
    const selectedDevices = ref([]);
    const waterDeviceTypes = ref([])
    const editRules = {
        equipmentCode: [{required: true, message: '请输入设备编码', trigger: 'blur'}],
        equipmentTypeId: [{required: true, message: '请选择设备类型', trigger: 'change'}],
        equipmentName: [{required: true, message: '请输入设备名称', trigger: 'blur'}]
    }
    const page = ref({records: [], total: 0});
    const ledgerStats = ref({total: 0, online: 0, offline: 0, onlineRate: 0});
    const loading = ref(false);
    const loadError = ref('');
    const detailVisible = ref(false);
    const detail = ref({});
    const dispatchVisible = ref(false);
    const dispatching = ref(false);
    const dispatchDevice = ref({});
    const dispatchForm = ref({description: '', receiveUserId: undefined});
    const users = ref([]);
    const userLoading = ref(false);
    const userLoadError = ref('');
    const deptLoadError = ref('');
    const selectedAssignee = ref(null);
    const assigneeDraft = ref(null);
    const deptTreeRef = ref(null);
    const deptTreeData = ref([]);
    const deptFilterText = ref('');
    const assigneeKeyword = ref('');
    const assigneeDeptId = ref(undefined);
    const assigneePageNum = ref(1);
    const assigneePageSize = ref(10);
    const assigneeTotal = ref(0)
    const levels = [{label: '一级报警', value: 1}, {label: '二级报警', value: 2}, {
        label: '三级报警',
        value: 3
    }, {label: '四级报警', value: 4}];
    const pageIcon = Monitor;
    const alarmLevelType = level => ({1: 'danger', 2: 'warning', 3: 'primary', 4: 'success'}[Number(level)] || 'info')

    function canDispatch(row) {
        if (view !== 'dispatch' && view !== 'abnormal') return false
        return canDispatchDevice(row)
    }

    function workOrderStatusType(code) {
        return ({
            1: 'info',
            2: 'warning',
            3: 'primary',
            4: 'warning',
            5: 'danger',
            6: 'success',
            7: 'info',
            8: 'warning',
            9: 'warning'
        })[Number(code)] || 'info'
    }

    const abnormalStats = computed(() => {
        const rows = page.value.records
        const alarmCount = rows.filter(row => Number(row.alarmStatus) === 1).length
        const repairCount = rows.filter(row => Number(row.currentStatus) === 3).length
        const pendingCount = rows.filter(row => row.workOrderStatusCode && !row.workOrderTerminal).length
        return [
            {label: '异常设备', value: page.value.total, unit: '台', icon: Warning},
            {label: '报警中', value: alarmCount, unit: '台', icon: Bell},
            {label: '维修中', value: repairCount, unit: '台', icon: Tools},
            {label: '待闭环', value: pendingCount, unit: '条', icon: DataLine}
        ]
    })
    const ledgerStatsCards = computed(() => [
        {label: '设备数量', value: ledgerStats.value.total, unit: '台', icon: Monitor},
        {label: '在线设备', value: ledgerStats.value.online, unit: '台', icon: DataLine},
        {label: '离线设备', value: ledgerStats.value.offline, unit: '台', icon: Warning},
        {label: '在线率', value: ledgerStats.value.onlineRate, unit: '%', icon: Tools}
    ])

    const detailFields = computed(() => [['设备编码', detail.value.equipmentCode], ['设备类型', detail.value.equipmentTypeName || detail.value.typeName], ['安装位置', detail.value.equipmentLocation], ['设备状态', detail.value.currentStatusText], ['在线状态', detail.value.onlineStatusText], ['报警状态', detail.value.alarmStatusText], ['报警等级', detail.value.alarmLevelText], ['异常原因', detail.value.abnormalReasonText], ['关联工单', detail.value.relatedOrderNo || detail.value.orderNo], ['工单状态', detail.value.workOrderStatusText || detail.value.status], ['接单责任人', detail.value.relatedWorkOrderPerson || detail.value.maintainPerson], ['状态更新时间', detail.value.statusUpdateTime]].map(([label, value]) => ({
        label,
        value: value ?? '-'
    })))

    async function loadData() {
        loading.value = true;
        loadError.value = '';
        try {
            if (view === 'gis') {
                const response = await listDeviceGisPoints({...query.value});
                const records = normalizeDeviceGisPoints(response).map(normalizeDeviceRow);
                page.value = {records, total: records.length, current: 1, size: records.length || 10};
                return
            }
            let response;
            if (view === 'maintain') response = await listMaintainRecords({...query.value}); else if (view === 'dispatch' || view === 'abnormal') response = await listAbnormalDevices({...query.value, ...(view === 'dispatch' ? {dispatchableOnly: true} : {})}); else response = await listDeviceLedger({...query.value});
            const normalized = normalizePage(response);
            page.value = {...normalized, records: normalized.records.map(normalizeDeviceRow)}
            if (view === 'ledger') await loadLedgerStats()
        } catch (error) {
            const retained = preserveDevicePageOnError(page.value);
            page.value = retained.page;
            loadError.value = retained.error;
            console.warn(`[${title}] 数据加载失败`, error)
        } finally {
            loading.value = false
        }
    }

    async function loadLedgerStats() {
        try {
            const response = await getDeviceLedgerStatistics()
            const stats = response?.data ?? response ?? {}
            ledgerStats.value = {
                total: Number(stats.total ?? 0) || 0,
                online: Number(stats.online ?? 0) || 0,
                offline: Number(stats.offline ?? 0) || 0,
                onlineRate: Number(stats.onlineRate ?? 0) || 0
            }
        } catch (error) {
            console.warn('设备台账统计加载失败', error)
        }
    }

    function reset() {
        query.value = {
            pageNum: 1,
            pageSize: 10,
            equipmentName: '',
            equipmentCode: '',
            alarmStatus: undefined,
            alertLevel: undefined
        };
        loadData()
    }

    function selectPoint(row) {
        detail.value = normalizeDeviceRow(row);
        detailVisible.value = true
    }

    watch(deptFilterText, value => deptTreeRef.value?.filter(value))

    function filterDeptNode(value, data) {
        return !value || String(data.deptName || '').includes(value)
    }

    async function loadDeptTree(force = false) {
        if (!force && deptTreeData.value.length) return true;
        deptLoadError.value = '';
        try {
            const response = await listDept();
            deptTreeData.value = handleTree(response.data || [], 'deptId');
            return true
        } catch (error) {
            deptLoadError.value = '部门列表加载失败，请点击重试';
            userLoadError.value = deptLoadError.value;
            return false
        }
    }

    async function loadAssigneeUsers() {
        userLoading.value = true;
        userLoadError.value = '';
        try {
            const response = await listUser(buildAssigneeQuery({
                deptId: assigneeDeptId.value,
                keyword: assigneeKeyword.value,
                pageNum: assigneePageNum.value,
                pageSize: assigneePageSize.value
            }));
            const rows = response.rows || response.data?.rows || [];
            users.value = rows.map(normalizeAssigneeUser).filter(user => user.eligible);
            assigneeTotal.value = Number(response.total ?? response.data?.total ?? users.value.length) || 0
        } catch (error) {
            userLoadError.value = '责任人列表加载失败，请点击重试'
        } finally {
            userLoading.value = false
        }
    }

    function searchAssigneeUsers() {
        assigneePageNum.value = 1;
        loadAssigneeUsers()
    }

    function handleAssigneeSizeChange() {
        assigneePageNum.value = 1;
        loadAssigneeUsers()
    }

    async function retryAssigneeLoading() {
        const deptLoaded = await loadDeptTree(true);
        if (!deptLoaded) return;
        await loadAssigneeUsers()
    }

    async function openOrder(row) {
        dispatchDevice.value = row
        dispatchForm.value = {
            description: row.abnormalReason || row.abnormalReasonText || row.description || '',
            orderLevel: 2,
            receiveUserId: undefined
        }
        selectedAssignee.value = null
        assigneeDraft.value = null
        assigneeDeptId.value = undefined
        assigneeKeyword.value = ''
        assigneePageNum.value = 1
        dispatchVisible.value = true
        await retryAssigneeLoading()
    }

    function handleDeptClick(node) {
        assigneeDeptId.value = node.deptId;
        assigneePageNum.value = 1;
        loadAssigneeUsers()
    }

    function chooseAssignee(row) {
        assigneeDraft.value = selectSingleAssignee([row])
    }

    function confirmAssignee() {
        selectedAssignee.value = assigneeDraft.value;
        dispatchForm.value.receiveUserId = assigneeDraft.value?.userId
    }

    async function submitDispatch() {
        if (!dispatchForm.value.description?.trim()) return ElMessage.warning('请填写问题描述')
        if (!dispatchForm.value.receiveUserId) return ElMessage.warning('请选择接单责任人')
        dispatching.value = true
        try {
            const response = await createAbnormalOrder(buildMaintainOrderPayload(dispatchDevice.value, dispatchForm.value))
            const summary = normalizeDispatchOrderResponse(response)
            if (!summary) throw new Error('派单失败：服务端未返回有效的待接单工单')
            ElMessage.success(`已派单，等待接单（${summary.orderNo}）`)
            dispatchVisible.value = false
            await loadData()
        } catch (error) {
            const responseData = error?.response?.data
            const message = responseData?.msg || responseData?.message || error?.message || '派单失败，请检查权限或服务状态'
            // A conflict means another order now owns the device; refresh the list so its order number is visible.
            if (/已有|冲突|重复|处理中的工单|关联工单/.test(String(message))) {
                await loadData()
                ElMessage.warning(`该设备已有相关处置工单：${message}`)
            } else {
                ElMessage.error(message)
            }
        } finally {
            dispatching.value = false
        }
    }

    async function removeRecord(row) {
        try {
            await deleteMaintainRecord(row.maintainId || row.id);
            ElMessage.success('记录已删除');
            await loadData()
        } catch (error) {
            ElMessage.error('删除失败')
        }
    }

    function emptyDeviceForm() {
        return {
            equipmentId: undefined, equipmentCode: '', equipmentTypeId: undefined, equipmentTypeName: '',
            equipmentName: '', equipmentModel: '', district: '', manufacturer: '', longitude: undefined,
            latitude: undefined, equipmentLocation: '', maintainer: '', maintainerPhone: '', ownershipUnit: '',
            equipmentImage: '', remark: ''
        }
    }

    function isWaterDeviceType(type, typesById) {
        if (!type || (type.typeName === '供水' && type.parentTypeId === '0')) return false
        let cursor = type
        let guard = 0
        while (cursor && guard++ <= typesById.size) {
            if (cursor.typeName === '供水' && cursor.parentTypeId === '0') return true
            cursor = typesById.get(cursor.parentTypeId)
        }
        return false
    }

    async function loadWaterTypes() {
        if (waterDeviceTypes.value.length) return
        try {
            const response = await listEquipmentTypeAll()
            const rows = response?.data || []
            const typesById = new Map(rows.map(item => [item.id, item]))
            waterDeviceTypes.value = rows.filter(item => isWaterDeviceType(item, typesById))
                .map(({id, typeName}) => ({id, typeName}))
        } catch (error) {
            ElMessage.warning('供水设备类型加载失败，请重试')
        }
    }

    async function openCreate() {
        editMode.value = 'create'
        editVisible.value = true
        editSaving.value = false
        editForm.value = emptyDeviceForm()
        await loadWaterTypes()
    }

    function handleDeviceSelectionChange(rows) {
        selectedDevices.value = rows
    }

    async function confirmDeleteDevices(ids) {
        await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 台设备吗？删除后不可恢复。`, '删除设备', {type: 'warning'})
        await deleteWaterDevices(ids)
        ElMessage.success('删除成功')
        selectedDevices.value = []
        query.value.pageNum = 1
        await loadData()
    }

    async function removeDevice(row) {
        if (!row?.equipmentId) return
        await confirmDeleteDevices([row.equipmentId])
    }

    async function removeSelectedDevices() {
        const ids = selectedDevices.value.map(row => row.equipmentId).filter(Boolean)
        if (!ids.length) return
        await confirmDeleteDevices(ids)
    }

    async function openEdit(row) {
        editMode.value = 'edit'
        editVisible.value = true
        editSaving.value = false
        editForm.value = {...row}
        try {
            const response = await getWaterDeviceById(row.equipmentId)
            const full = response?.data || response
            if (full) editForm.value = {...editForm.value, ...full}
        } catch (error) {
            ElMessage.warning('设备详情加载失败，已使用列表数据填充')
        }
    }

    async function submitEdit() {
        try {
            await editFormRef.value?.validate()
        } catch {
            return
        }
        editSaving.value = true
        try {
            const creating = editMode.value === 'create'
            if (creating) await createWaterDevice(editForm.value)
            else await updateWaterDevice(editForm.value)
            ElMessage.success(creating ? '新增成功' : '修改成功')
            editVisible.value = false
            if (creating) query.value.pageNum = 1
            await loadData()
        } catch (error) {
            ElMessage.error(error?.response?.data?.msg || error?.message || (editMode.value === 'create' ? '新增失败，请稍后重试' : '修改失败，请稍后重试'))
        } finally {
            editSaving.value = false
        }
    }

    function openImport() {
        importVisible.value = true
    }

    function handleImportChange(file, fileList) {
        if (!/\.(xlsx|xls)$/iu.test(file?.name || '')) {
            ElMessage.error('仅支持 xls、xlsx 文件')
            importFileList.value = []
            return false
        }
        importFileList.value = fileList.slice(-1)
    }

    function handleImportRemove(file, fileList) {
        importFileList.value = fileList
    }

    function resetImport() {
        importUploading.value = false
        importUpdateSupport.value = false
        importFileList.value = []
        importUploadRef.value?.clearFiles()
    }

    async function submitImport() {
        if (!importFileList.value.length) return ElMessage.warning('请选择 xls 或 xlsx 文件')
        importUploading.value = true
        try {
            const response = await importDeviceLedger(importFileList.value[0].raw, importUpdateSupport.value)
            if (response?.code && response.code !== 200) throw new Error(response.msg || response.message || '导入失败，请检查文件内容')
            ElMessage.success(response?.msg || response?.data || '导入成功')
            importVisible.value = false
            await loadData()
        } catch (error) {
            ElMessage.error(error?.response?.data?.msg || error?.response?.data?.message || error?.message || '导入失败，请稍后重试')
        } finally {
            importUploading.value = false
        }
    }

    async function exportData() {
        try {
            const response = await (view === 'dispatch' || view === 'abnormal' ? exportAbnormalDevices : exportDeviceLedger)({...query.value})
            const errorMessage = await readMonitoringExportError(response)
            if (errorMessage) throw new Error(errorMessage)
            const blob = response instanceof Blob ? response : new Blob([response], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
            saveAs(blob, `${title}_${new Date().toISOString().slice(0, 10)}.xlsx`)
            ElMessage.success('导出成功')
        } catch (error) {
            const backendMessage = await readMonitoringExportError(error?.response?.data)
            ElMessage.error(backendMessage || error?.message || '导出失败，请稍后重试')
        }
    }

    onMounted(loadData)
</script>

<style scoped>
    .device-page {
        padding: 20px;
        background: linear-gradient(135deg, #eff6ff 0%, #f8fafc 45%, #eef2f7 100%);
        min-height: calc(100vh - 84px)
    }

    .page-heading {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 16px;
        margin-bottom: 16px
    }

    .page-title {
        display: flex;
        align-items: center;
        gap: 12px;
        min-width: 0
    }

    .page-title-icon {
        display: grid;
        place-items: center;
        width: 42px;
        height: 42px;
        color: #fff;
        background: linear-gradient(135deg, #2563eb 0%, #06b6d4 100%);
        border-radius: 8px;
        box-shadow: 0 8px 16px rgba(37, 99, 235, .2)
    }

    .page-title h2 {
        margin: 0 0 4px;
        font-size: 20px;
        color: #1e293b
    }

    .muted {
        font-size: 13px;
        color: #64748b
    }

    .heading-refresh {
        border-color: #bfdbfe;
        color: #2563eb;
        background: #fff
    }

    .filter-card, .water-panel {
        border: 1px solid #e2e8f0;
        border-radius: 8px;
        box-shadow: 0 6px 14px rgba(15, 23, 42, .04)
    }

    .filter-card {
        margin-bottom: 16px;
        background: #fff
    }

    .filter-card :deep(.el-card__header) {
        padding: 14px 18px;
        border-bottom: 1px solid #e2e8f0;
        background: #fbfdff
    }

    .filter-card :deep(.el-form-item) {
        margin-top: 8px;
        margin-right: 16px;
        margin-bottom: 8px
    }

    .filter-card :deep(.el-form-item__label) {
        color: #475569
    }

    .metric-grid {
        display: grid;
        grid-template-columns:repeat(4, minmax(0, 1fr));
        gap: 14px;
        margin-bottom: 16px
    }

    .metric {
        display: flex;
        align-items: center;
        gap: 14px;
        min-width: 0;
        padding: 16px 18px;
        background: #fff;
        border: 1px solid #e2e8f0;
        border-radius: 8px;
        box-shadow: 0 6px 14px rgba(15, 23, 42, .04)
    }

    .metric-icon {
        position: static;
        flex: 0 0 42px;
        display: grid;
        place-items: center;
        width: 42px;
        height: 42px;
        color: #2563eb;
        background: linear-gradient(135deg, #dbeafe 0%, #eff6ff 100%);
        border-radius: 8px
    }

    .metric-icon .el-icon {
        font-size: 22px
    }

    .metric-body {
        min-width: 0;
        text-align: left
    }

    .metric-body span {
        display: block;
        overflow: hidden;
        margin-bottom: 3px;
        color: #64748b;
        font-size: 13px;
        text-overflow: ellipsis;
        white-space: nowrap
    }

    .metric-value {
        display: flex;
        align-items: baseline;
        gap: 5px;
        min-width: 0
    }

    .metric-value strong {
        font-size: 24px;
        line-height: 1.2;
        color: #1e293b
    }

    .metric-value small {
        color: #94a3b8
    }

    .water-panel {
        overflow: hidden
    }

    .water-panel :deep(.el-card__header) {
        padding: 14px 18px;
        border-bottom: 1px solid #e2e8f0;
        background: #fbfdff
    }

    .card-title {
        display: flex;
        align-items: center;
        gap: 8px;
        color: #1e293b;
        font-weight: 600;
        margin-bottom: 16px
    }

    .card-title .el-icon {
        color: #2563eb;
        font-size: 18px
    }

    .table-toolbar {
        display: flex;
        justify-content: space-between;
        align-items: center
    }

    .panel-actions {
        display: flex;
        align-items: center;
        gap: 8px
    }

    .load-error {
        margin-bottom: 14px
    }

    .device-page :deep(.el-table) {
        --el-table-border-color: #e2e8f0;
        --el-table-header-bg-color: #f8fafc;
        --el-table-header-text-color: #475569;
        --el-table-row-hover-bg-color: #eff6ff
    }

    .device-page :deep(.el-table__row) {
        cursor: pointer
    }

    .device-image {
        width: 66px;
        height: 44px;
        border-radius: 6px;
        overflow: hidden
    }

    .image-placeholder {
        display: inline-grid;
        place-items: center;
        width: 66px;
        height: 44px;
        color: #94a3b8;
        font-size: 12px;
        background: #f1f5f9;
        border-radius: 6px
    }

    .detail-summary {
        display: flex;
        align-items: center;
        gap: 12px;
        margin-bottom: 16px
    }

    .detail-name {
        font-size: 18px;
        font-weight: 700;
        color: #17324d
    }

    .device-detail-drawer :deep(.el-drawer__body), .device-edit-drawer :deep(.el-drawer__body) {
        overflow: auto;
        padding: 20px 24px
    }

    .device-detail-drawer :deep(.el-descriptions__label) {
        white-space: nowrap
    }

    .device-detail-drawer :deep(.el-descriptions__content) {
        overflow-wrap: anywhere;
        word-break: break-word
    }

    .detail-media {
        margin-bottom: 14px;
        padding: 12px;
        background: #f7fbff;
        border: 1px solid rgba(37, 99, 235, .1);
        border-radius: 10px
    }

    .detail-media-label {
        margin-bottom: 8px;
        font-weight: 600;
        color: #17324d
    }

    .detail-image {
        width: 100%;
        height: 180px;
        border-radius: 8px;
        overflow: hidden
    }

    .detail-image-placeholder {
        display: grid;
        place-items: center;
        height: 120px;
        background: #f1f5f9;
        border-radius: 8px;
        color: #909399
    }

    .full-input {
        width: 100%
    }

    .import-update {
        margin-top: 10px
    }

    .import-tip {
        line-height: 1.6
    }

    .el-pagination {
        justify-content: flex-end;
        margin-top: 16px
    }

    .dispatch-layout {
        display: flex;
        gap: 20px;
        min-height: 420px
    }

    .dispatch-form {
        width: 300px;
        min-width: 280px;
        padding-left: 20px;
        border-left: 1px solid #e4e7ed
    }

    .assignee-field {
        display: flex;
        align-items: center;
        gap: 12px;
        width: 100%;
        min-height: 32px
    }

    .assignee-summary {
        color: #17324d
    }

    .assignee-placeholder {
        color: #909399
    }

    .assignee-picker {
        display: flex;
        gap: 16px;
        flex: 1;
        min-width: 0;
        min-height: 390px
    }

    .assignee-depts {
        width: 190px;
        min-width: 190px;
        padding-right: 16px;
        border-right: 1px solid #e4e7ed
    }

    .assignee-tree {
        margin-top: 10px;
        max-height: 340px;
        overflow: auto
    }

    .assignee-users {
        flex: 1;
        min-width: 0
    }

    .assignee-search {
        display: flex;
        gap: 8px;
        margin-bottom: 12px
    }

    .assignee-search .el-input {
        flex: 1
    }

    .assignee-users .el-pagination {
        margin-top: 12px
    }

    .assignee-picked {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 8px;
        margin-top: 14px;
        padding: 10px 12px;
        background: #f5f7fa;
        border: 1px solid #ebeef5;
        color: #606266
    }

    .assignee-picked strong {
        color: #2563eb
    }

    .assignee-users .el-alert {
        margin-bottom: 12px
    }

    @media (max-width: 900px) {
        .metric-grid {
            grid-template-columns:repeat(2, minmax(0, 1fr))
        }

        .filter-card :deep(.el-form-item) {
            margin-bottom: 12px
        }
    }

    @media (max-width: 768px) {
        .device-detail-drawer, .device-edit-drawer {
            width: 100% !important
        }

        .dispatch-dialog {
            width: calc(100% - 24px) !important;
            margin-left: auto;
            margin-right: auto
        }

        .dispatch-layout {
            flex-direction: column
        }

        .assignee-picker {
            flex-direction: column
        }

        .assignee-depts {
            width: auto;
            min-width: 0;
            padding-right: 0;
            padding-bottom: 12px;
            border-right: 0;
            border-bottom: 1px solid #e4e7ed
        }

        .assignee-tree {
            max-height: 180px
        }

        .dispatch-form {
            width: auto;
            min-width: 0;
            padding-left: 0;
            padding-top: 16px;
            border-left: 0;
            border-top: 1px solid #e4e7ed
        }
    }
</style>
<style lang="scss">
    @use './waterSupplyPageTheme.scss';
    @media (max-width: 768px) {
        .device-detail-drawer {
            width: 100% !important
        }

        .dispatch-dialog {
            width: calc(100% - 24px) !important;
            margin-left: auto;
            margin-right: auto
        }

        .dispatch-layout {
            flex-direction: column
        }

        .assignee-picker {
            flex-direction: column
        }

        .assignee-depts {
            width: auto;
            min-width: 0;
            padding-right: 0;
            padding-bottom: 12px;
            border-right: 0;
            border-bottom: 1px solid #e4e7ed
        }

        .assignee-tree {
            max-height: 180px
        }

        .dispatch-form {
            width: auto;
            min-width: 0;
            padding-left: 0;
            padding-top: 16px;
            border-left: 0;
            border-top: 1px solid #e4e7ed
        }
    }
</style>
