<template>
  <!--基本信息-->
  <ContentWrap>
    <div class="basicInformation">
      <!--      <section class="topTitle">-->
      <div class="tab-header">
        <section class="topTitle_left">
          <img src="@/assets/images/information.png" alt="" class="titleimg" />
          <span class="tab-title"> 基本信息</span>
        </section>
        <div class="set-btn" @click="goBack">
          <img src="../../../assets/images/返回.png" alt="" />
          返回
        </div>
      </div>

      <div class="basicContent">
        <p
          >作业名称： <span>{{ JobForm.ticketName }}</span></p
        >
        <p
          >作业区域: <span>{{ JobForm.workstationName }}</span></p
        >
        <p
          >工艺设备: <span>{{ JobForm.machineryName }}</span></p
        >
        <p
          >作业类型: <span>{{ ticketTypeLabel }}</span></p
        >
        <p
          >作业状态: <span>{{ ticketStatusLabel }}</span></p
        >
        <p
          >开始时间: <span>{{ JobForm.ticketStartTime || '-' }}</span></p
        >
        <p
          >结束时间: <span>{{ JobForm.ticketEndTime || '-' }} </span></p
        >
        <p
          >当前步骤: <span class="specialText">{{ currentStepTitle }}</span></p
        >
        <p
          >最新日志:
          <span
            v-if="latestLog"
            class="hh"
            v-html="renderLogContent(latestLog.operationContent)"
          ></span>
        </p>
      </div>
    </div>
  </ContentWrap>
  <!--作业流程-->
  <ContentWrap>
    <div class="jobProcess">
      <div class="tab-header">
        <img src="@/assets/images/jobProcess.png" alt="" class="titleimg" /><span class="tab-title"
          >作业流程</span
        >
      </div>
      <div class="processDetail" ref="scrollContainer">
        <!-- VueFlow 主画布 -->
        <VueFlow
          style="width: 100%; height: 300px"
          :min-zoom="1"
          :max-zoom="1"
          :default-zoom="1"
          :nodes="nodes"
          :edges="edges"
        >
          <template #node-default="{ id, data }">
            <div class="custom-node" :id="id">
              <div class="node-content">
                <!-- 图标显示 -->
                <div style="font-size: 30px">
                  <img
                    v-if="data.stepIcon && data.stepIcon.startsWith('http')"
                    :src="data.stepIcon"
                    :alt="data.stepTitleShort"
                    style="width: 40px; height: 40px; object-fit: contain"
                  />
                  <span v-else>{{ data.stepIcon || '📋' }}</span>
                </div>
                <div style="font-weight: bold; font-size: 14px">
                  {{ data.stepTitleShort || '无标题' }}
                </div>
                <div style="font-size: 25px">
                  {{ String.fromCharCode(9311 + (data.stepIndex || 1)) }}
                </div>
              </div>
              <!-- 四个连接点 -->
              <Handle type="target" position="top" :id="`${id}-top`" class="handle handle-top" />
              <Handle
                type="source"
                position="bottom"
                :id="`${id}-bottom`"
                class="handle handle-bottom"
              />
              <Handle type="target" position="left" :id="`${id}-left`" class="handle handle-left" />
              <Handle
                type="source"
                position="right"
                :id="`${id}-right`"
                class="handle handle-right"
              />
            </div>
          </template>
        </VueFlow>
      </div>
    </div>
  </ContentWrap>
  <!--作业执行-->
  <ContentWrap style="overflow: auto">
    <div class="jobExecution">
      <el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick">
        <!-- 作业人员-->
        <el-tab-pane name="first">
          <template #label>
            <div class="tab-label">
              <img src="@/assets/images/icon_job_members.png" alt="" class="titleimg" />
              <span class="tab-text">作业人员</span>
            </div>
          </template>
          <div class="jobMemberBox">
            <!-- 锁定人区域 -->
            <div class="left_box">
              <div class="tab-header">
                <span class="tab-title">锁定人</span>
              </div>

              <!-- 有锁定人数据时显示 -->
              <div v-if="groupedLockers.length" class="group-container-user">
                <div v-for="group in groupedLockers" :key="group.groupId" class="group-card-user">
                  <div class="group-title">{{ group.groupName }}</div>
                  <div class="user-list">
                    <div v-for="user in group.users" :key="user.userId" class="user-card">
                      <img src="@/assets/images/UserBlack.png" />
                      <div class="user-name">{{ user.userName }}</div>
                    </div>
                  </div>
                </div>
              </div>
              <p v-show="groupedLockers.length == 0" style="margin-top: 20px; text-align: center">
                暂无数据</p
              >
            </div>
            <!-- 共锁人区域 -->
            <div class="right_box">
              <div class="tab-header">
                <span class="tab-title">共锁人</span>
              </div>
              <div v-if="coLockUsers.length" class="user-list-colocker">
                <div v-for="user in coLockUsers" :key="user.userId" class="user-card">
                  <img src="@/assets/images/UserBlack.png" />
                  <div class="user-name">{{ user.userName }}</div>
                </div>
              </div>
              <p v-show="coLockUsers.length == 0" style="margin-top: 20px; text-align: center">
                暂无数据</p
              >
            </div>
          </div>
        </el-tab-pane>
        <!--点位锁定-->
        <el-tab-pane name="second">
          <template #label>
            <div class="tab-label">
              <img src="@/assets/images/icon_red_lock.png" alt="" class="titleimg" />
              <span class="tab-text">点位锁定</span>
            </div>
          </template>
          <div class="jobMemberBox">
            <div class="group-box" v-for="(group, index) in jobGroupList" :key="group.id">
              <div class="tab-header">
                <p class="tab-title"
                  >{{ group.groupName }}(待锁定：{{ group.waitLock }} 已锁定：{{
                    group.locked
                  }}
                  已解锁：{{ group.unlocked }})</p
                >
              </div>
              <!-- 表格数据 -->
              <div class="tableCon">
                <el-radio-group v-model="tableLayouts[index]">
                  <el-radio-button value="fixed">常规</el-radio-button>
                  <el-radio-button value="auto">扩展</el-radio-button>
                </el-radio-group>
                <el-table :data="group.ticketPointsRespVOList" :table-layout="tableLayouts[index]">
                  <el-table-column prop="pointName" label="隔离点" />
                  <el-table-column prop="ability" label="作用" />
                  <el-table-column prop="lockUserName" label="锁定人" />
                  <el-table-column prop="pointStatus" label="锁定状态">
                    <template #default="scope">
                      <dict-tag :type="DICT_TYPE.POINT_STATUS" :value="scope.row.pointStatus" />
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
        </el-tab-pane>
        <!-- 人员共锁 -->
        <el-tab-pane name="third" v-if="isShowclocker">
          <template #label>
            <div class="tab-label">
              <img src="@/assets/images/icon_co-lock.png" alt="" class="titleimg" />
              <span class="tab-text">人员共锁</span>
            </div>
          </template>
          <div class="biglockBox">
            <!-- 待共锁-->
            <div class="mumberbox">
              <div class="tab-header">
                <span class="tab-title">待共锁（{{ waitLockUsers.length }}）</span>
              </div>
              <div class="user">
                <div class="userItem" v-for="user in waitLockUsers" :key="user.userId">
                  <img :src="user.avatar" alt="" />
                  <p>{{ user.userName }}</p>
                </div>
              </div>
            </div>
            <el-icon :size="50" :color="color" class="arrow">
              <CaretRight />
            </el-icon>
            <!-- 已共锁-->
            <div class="mumberbox">
              <div class="tab-header">
                <span class="tab-title">已共锁（{{ lockedUsers.length }}）</span>
              </div>
              <div class="user">
                <div class="userItem" v-for="user in lockedUsers" :key="user.userId">
                  <img :src="user.avatar" alt="" />
                  <p>{{ user.userName }}</p>
                </div>
              </div>
            </div>
            <el-icon :size="50" :color="color" class="arrow">
              <CaretRight />
            </el-icon>
            <!-- 已解除共锁-->
            <div class="mumberbox">
              <div class="tab-header">
                <span class="tab-title">已解除共锁（{{ unlockPendingUsers.length }}）</span>
              </div>
              <div class="user">
                <div class="userItem" v-for="user in unlockPendingUsers" :key="user.userId">
                  <img :src="user.avatar" alt="" />
                  <p>{{ user.userName }} </p>
                </div>
              </div>
            </div>
          </div>
        </el-tab-pane>
        <!--作业日志-->
        <el-tab-pane name="fourth">
          <template #label>
            <div class="tab-label">
              <img src="@/assets/images/icon_job_log.png" alt="" class="titleimg" />
              <span class="tab-text">作业日志</span>
            </div>
          </template>
          <p v-show="filteredJoblogList.length == 0" style="margin-top: 20px; text-align: center">
            暂无数据</p
          >
          <div class="joblogCon" v-show="filteredJoblogList.length > 0">
            <!--顶部日志内容-->
            <div class="joblogTop">
              <p v-for="(item, index) in filteredJoblogList" :key="index">
                <span v-html="renderLogContent(item.operationContent)"></span>
              </p>
            </div>
            <!-- 底部过滤条件-->
            <div class="bottomCheck">
              <el-checkbox
                :indeterminate="isIndeterminate"
                :checked="checkAlljoblog"
                @change="handleCheckAllChange"
                style="margin: 0 25px; font-size: 16px"
              >
                全部
              </el-checkbox>

              <!-- 多选项 -->
              <el-checkbox-group
                v-model="checkedJoblogs"
                @change="handleCheckedJoblogsChange"
                class="big-checkbox"
              >
                <el-checkbox
                  v-for="item in jobLogtypes"
                  :label="item.value"
                  :key="item.value"
                  class="big-checkbox"
                >
                  {{ item.label }}
                </el-checkbox>
              </el-checkbox-group>
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>
    </div>
  </ContentWrap>
  <!--  作业日志弹框-->
  <el-dialog v-model="JoblogDialogVisible" title="新日志提醒" width="500" align-center>
    <p>
      <span v-html="renderLogContent(dialogLog.operationContent)"></span>
    </p>
  </el-dialog>
</template>

<script setup lang="ts">
import { ref, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { Handle, useVueFlow, VueFlow } from '@vue-flow/core'
import * as JobPointGroup from '@/api/job/jobPointGroup'
import * as PointApi from '@/api/dv/spm/index'
import * as JobApi from '@/api/job/index'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import type { TableInstance } from 'element-plus'
import { CaretRight } from '@element-plus/icons-vue'
import { getTicketOperLogPage } from '@/api/job/index'
import { connectWebsocket, closeWebsocket } from '@/utils/webSocket'
import { getIsSystemAttributeByKey } from '@/api/basic/configuration/index'
import type { TabsPaneContext } from 'element-plus'


const router = useRouter()
const JobForm = reactive({
  createTime: null,
  id: null,
  machineryId: null,
  machineryName: null,
  modeId: null,
  sopGroupList: null,
  sopIndex: null,
  sopName: null,
  workstationId: null,
  workstationName: null,
  ticketCode: null,
  ticketName: null,
  sopId: null,
  ticketType: null,
  ticketContent: null,
  ticketStatus: null,
  ticketStartTime: null,
  ticketEndTime: null,
  ticketGroupList: null,
  ticketPointsList: null,
  ticketStepList: null,
  ticketUserList: null,
  ticketOperLogList: null
})
const allGroups = ref<any[]>([]) //获取所有分组
const groupList = ref([]) //获取当前sopId的分组
const allPoints = ref<any[]>([]) //获取所有点位
const activeName = ref('first')
const route = useRoute()
const jobTypeOptions = ref([])
const jobStatusOptions = ref([])
const currentStepTitle = ref('') // 当前步骤名称

// 表格数据
const jobGroupList = ref([]) //获取作业分组信息
const ticketPointsList = ref([]) // 隔离点数据
const tableLayouts = ref([]) // 每组表格的布局

// 人员共锁
const jobclockerList = ref([])
const isShowclocker = ref(true) //是否展示人员共锁
const waitLockUsers = ref([])
const lockedUsers = ref([])
const unlockPendingUsers = ref([])

// 作业日志
const joblogList = ref([]) //原始作业日志数据列表
const filteredJoblogList = ref([]) // 过滤后日志
//   作业日志底部查询
const isIndeterminate = ref(false)
const checkedJoblogs = ref([])
const jobLogtypes = ref([])
const joblogsOptions = ref([]) // 只存所有 dictValue，用于全选逻辑,
const checkAlljoblog = ref(true)
const dialogLog = ref('')
const isJoblogVisible = ref(false)
const JoblogDialogVisible = ref(false)

// 初始化
onMounted(async () => {
  await getDetail()
  //基础信息
  jobTypeOptions.value = await getIntDictOptions(DICT_TYPE.TICKET_TYPE)
  jobStatusOptions.value = await getIntDictOptions(DICT_TYPE.TICKET_STATUS)
  await fetchAllGroupsAndPoints() //获取所有分组和点位 来渲染SOP的首页
  await onModeContent() //作业流程
  window.addEventListener('resize', onResize)
  onResize()
  getWebSocket()
  const todoType = route.query.todoType as string | undefined
  if (!todoType) {
    // 没传值、undefined、null、空字符串都进这里
    activeName.value = 'first'
  } else if (todoType == 'CONFIRM' || todoType == 'END') {
    activeName.value = 'fourth'
    await handleClick({ paneName: 'fourth' } as TabsPaneContext, new Event('click'))
  } else {
    activeName.value = 'second'
  }
})
onBeforeUnmount(() => {
  window.removeEventListener('resize', onResize)
  closeWebsocket()
})
console.log(route.query.id, '是否传递成功')

// 基础信息作业类型
const ticketTypeLabel = computed(() => {
  const match = jobTypeOptions.value.find((item) => item.value == JobForm.ticketType)
  return match ? match.label : JobForm.ticketType
})
// 基础信息作业状态
const ticketStatusLabel = computed(() => {
  const match = jobStatusOptions.value.find((item) => item.value == JobForm.ticketStatus)
  return match ? match.label : JobForm.ticketStatus
})
// 基础信息最新日志
const latestLog = computed(() => {
  if (!joblogList.value || joblogList.value.length === 0) return null
  return joblogList.value.reduce((max, item) => (item.id > max.id ? item : max))
})

// 初始化详情数据
const getDetail = async () => {
  const JobData = await JobApi.selectJobTicketById(route.query.id)
  // 将JobData的数据复制给JobForm
  Object.assign(JobForm, JobData)
  // 点位锁定
  jobGroupList.value = JobForm.ticketGroupList
  ticketPointsList.value = JobForm.ticketGroupList.ticketPointsRespVOList

  console.log(
    JobData,
    '数据有哪些',
    ticketPointsList.value,
    '隔离点数据',
    jobGroupList.value,
    '分组信息'
  )
  // 初始化每个表格的布局设置为 'fixed'
  tableLayouts.value = jobGroupList.value.map(() => 'fixed')
  //   人员共锁
  jobclockerList.value = JobForm.ticketUserList
  // 判断是否存在共锁人
  isShowclocker.value = jobclockerList.value.some((item) => item.userRole === 'jtcolocker')
  updateUserGroups() // 分类数据
  //   作业日志
  joblogList.value = JobForm.ticketOperLogList
  // ✨强制刷新筛选后的列表
  filterJoblogs()
}

// 作业流程绘制
const scrollContainer = ref(null)
const nodes = ref([]) //储存节点
const edges = ref([]) // 存储连接线
const allSteps = ref([]) // 缓存完整流程数据
const { addNodes, addEdges, setEdges, setNodes } = useVueFlow()

const scrollToCurrentNode = () => {
  nextTick(() => {
    const container = scrollContainer.value
    if (!container) return

    const currentIndex = nodes.value.findIndex((n) => n.data.stepStatus === 0)
    if (currentIndex === -1) return

    const currentNodeId = nodes.value[currentIndex]?.id
    const nextNodeId = nodes.value[currentIndex + 1]?.id

    const currentNodeEl = document.getElementById(currentNodeId)
    const nextNodeEl = nextNodeId ? document.getElementById(nextNodeId) : null

    if (!currentNodeEl) return

    // 获取节点相对于容器的偏移位置
    const currentLeft = currentNodeEl.offsetLeft
    const currentWidth = currentNodeEl.offsetWidth

    let centerPos = currentLeft + currentWidth / 2

    if (nextNodeEl) {
      const nextLeft = nextNodeEl.offsetLeft
      const nextWidth = nextNodeEl.offsetWidth
      centerPos = (centerPos + nextLeft + nextWidth / 2) / 2
    }

    // 目标是让 centerPos 居中容器
    const scrollTarget = centerPos - container.clientWidth / 2

    container.scrollTo({
      right: scrollTarget,
      behavior: 'smooth'
    })
  })
}

const onResize = () => {
  const isMobile = window.innerWidth <= 600
  const stepsData = [...allSteps.value] // 缓存的完整流程步骤数据

  const currentIndex = stepsData.findIndex((item) => item.stepStatus === 0)
  const filtered =
    isMobile && currentIndex !== -1
      ? stepsData.slice(currentIndex) // 小屏只显示当前及后续
      : stepsData // 大屏恢复全部

  renderNodesFromData(filtered)
  renderEdgesFromData(filtered)

  nextTick(() => {
    scrollToCurrentNode()
  })
}

// 模式初始化
const onModeContent = async (value) => {
  JobForm.modeId = value
  await clearCanvasProperly()
  const Data = await JobApi.selectJobTicketById(route.query.id)
  const sortedData = Data.ticketStepList.sort((a, b) => (a.stepIndex || 0) - (b.stepIndex || 0))
  renderNodesFromData(sortedData)
  renderEdgesFromData(sortedData)
  allSteps.value = sortedData // 缓存原始数据
  renderResponsiveSteps()
}

// 屏幕缩小节点展示
const renderResponsiveSteps = () => {
  const isMobile = window.innerWidth <= 600
  const stepsData = [...allSteps.value]

  const currentIndex = stepsData.findIndex((item) => item.stepStatus === 0)
  const filtered =
    isMobile && currentIndex !== -1
      ? stepsData.slice(currentIndex) // 从当前步骤开始
      : stepsData // 全部步骤

  renderNodesFromData(filtered)
  renderEdgesFromData(filtered)

  nextTick(() => {
    scrollToCurrentNode()
  })
}

// 独立的清空画布函数
const clearCanvasProperly = async () => {
  setNodes([])
  setEdges([])
  nodes.value = [] // 同步响应式数据（如果有自定义 nodes）
  edges.value = []
}
// 初始化数据渲染节点 - 修正版本
const renderNodesFromData = (data) => {
  nodes.value = []

  const firstZeroIndex = data.findIndex((item) => item.stepStatus === 0)
  if (firstZeroIndex !== -1) {
    currentStepTitle.value = data[firstZeroIndex].stepTitleShort || '无标题'
  } else {
    currentStepTitle.value = ''
  }

  data.forEach((item, index) => {
    let backgroundColor = '#fff'
    let isCurrent = false
    if (item.stepStatus === 1) {
      backgroundColor = '#a0e99b'
    } else if (item.stepStatus === 0 && index === firstZeroIndex) {
      backgroundColor = '#ffe58f'
      isCurrent = true
    }

    const nodeId = `node-${item.id || Date.now() + index}`

    nodes.value.push({
      id: nodeId,
      position: { x: 100 + index * 200, y: 100 },
      width: 100,
      height: 150,
      data: {
        ...item,
        stepIcon: item.stepIcon,
        stepTitleShort: item.stepTitleShort,
        stepIndex: item.stepIndex || index + 1,
        index: item.stepIndex || index + 1,
        stepData: item,
        bgColor: backgroundColor,
        isCurrent
      },
      style: {
        width: '130px',
        height: '180px',
        borderRadius: '12px',
        border: '1px solid #999',
        textAlign: 'center',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'space-between',
        padding: '10px',
        backgroundColor
      },
      draggable: false //设置是否可以拖动生成的图
    })
  })
  nextTick(() => {
    scrollToCurrentNode()
  })
}

// const renderNodesFromData = (data) => {
//   nodes.value = []
//
//   const firstZeroIndex = data.findIndex(item => item.stepStatus === 0)
//
//   // 设置当前步骤标题
//   if (firstZeroIndex !== -1) {
//     currentStepTitle.value = data[firstZeroIndex].stepTitleShort
//   } else {
//     currentStepTitle.value = '' // 或者显示“全部完成”之类的
//   }
//
//   data.forEach((item, index) => {
//     const nodeId = `node-${item.id || Date.now() + index}`
//
//     let backgroundColor = '#fff'
//     if (item.stepStatus === 1) {
//       backgroundColor = '#a0e99b'
//     } else if (item.stepStatus === 0 && index === firstZeroIndex) {
//       backgroundColor = '#ffe58f'
//     }
//
//     const newNode = {
//       id: nodeId,
//       position: {
//         x: 100 + index * 200,
//         y: 100
//       },
//       width: 100,
//       height: 150,
//       data: {
//         stepIcon: item.stepIcon,
//         stepTitleShort: item.stepTitleShort,
//         stepIndex: item.stepIndex || index + 1,
//         index: item.stepIndex || index + 1,
//         stepData: item
//       },
//       style: {
//         width: '130px',
//         height: '180px',
//         borderRadius: '12px',
//         border: '1px solid #999',
//         textAlign: 'center',
//         display: 'flex',
//         flexDirection: 'column',
//         alignItems: 'center',
//         justifyContent: 'space-between',
//         padding: '10px',
//         backgroundColor
//       },
//       draggable: true
//     }
//
//     addNodes(newNode)
//     nodes.value.push(newNode)
//   })
// }

// 渲染连接线 - 新增函数
const renderEdgesFromData = (data) => {
  // 清空现有连接线
  edges.value = []

  // 根据 stepIndex 顺序创建连接线
  for (let i = 0; i < data.length - 1; i++) {
    const currentStep = data[i]
    const nextStep = data[i + 1]

    const sourceNodeId = `node-${currentStep.id}`
    const targetNodeId = `node-${nextStep.id}`

    // 创建连接线，从右侧连接到左侧
    const edge = {
      id: `edge-${currentStep.id}-${nextStep.id}`,
      source: sourceNodeId,
      target: targetNodeId,
      sourceHandle: `${sourceNodeId}-right`, // 从右侧连接点出发
      targetHandle: `${targetNodeId}-left`, // 连接到左侧连接点
      type: 'smoothstep',
      style: { stroke: '#333', strokeWidth: 2 },
      markerEnd: {
        type: 'arrowclosed',
        width: 20,
        height: 20,
        color: '#333'
      }
    }

    addEdges(edge)
    edges.value.push(edge)

    console.log('创建连接线:', edge)
  }
}

// 底部tabbar点击事件
const handleClick = async (tab: TabsPaneContext, event: Event) => {
  // 当选中作业日志时传递isJoblogVisible给onNewSocketLog判断日志弹框的显示
  isJoblogVisible.value = tab.paneName === 'fourth'
  await getDetail()
  if (tab.paneName === 'fourth') {
    // console.log('作业日志')
    const dictRes = await getIntDictOptions(DICT_TYPE.JOB_LOG_TYPE)
    console.log(dictRes, '接口调用是否成功')
    const dicts = dictRes.map((item) => ({
      label: item.label,
      value: String(item.value) // 确保是字符串类型
    }))
    jobLogtypes.value = dicts
    joblogsOptions.value = dicts.map((item) => item.value)
    checkedJoblogs.value = [...joblogsOptions.value] // 初始化全选
    // console.log(checkAlljoblog.value, '选中状态')
    checkAlljoblog.value = true
    // console.log(checkAlljoblog.value, '是否选中')
    isIndeterminate.value = false
    filterJoblogs()
  }
}

//ticketType改变函数
const handleTicketpTypeChange = (value) => {
  JobForm.ticketType = value
}

// 获取所有点位和分组的数据
const fetchAllGroupsAndPoints = async () => {
  try {
    // 分组信息
    const groupRes = await JobPointGroup.getJobTicketGroupPage({ pageSize: -1, pageNo: 1 })
    allGroups.value = groupRes.list
    console.log('获取分组', groupRes.list)
    // 获取当前ticketId的分组
    if (route.query.id) {
      const groupData = await JobPointGroup.getJobTicketGroupPage({
        pageSize: -1,
        pageNo: 1,
        ticketId: route.query.id
      })
      groupList.value = groupData.list
    }

    // 点位信息
    const pointRes = await PointApi.getIsIsolationPointPage({ pageSize: -1, pageNo: 1 })
    allPoints.value = pointRes?.list
    console.log('获取点位', pointRes)
  } catch (e) {}
}
// 回显分组和点位数据的计算属性
const resolvedGroupedPoints = computed(() => {
  const groupsMap = new Map<string, { groupId: string; groupName: string; points: any[] }>()

  console.log('JobForm.sopPointsList:', JobForm.ticketPointsList)
  console.log('allGroups.value:', allGroups.value)
  console.log('allPoints.value:', allPoints.value)

  JobForm.ticketPointsList.forEach((item) => {
    const groupId = String(item.groupId)
    const pointId = item.pointId

    console.log('处理项目:', { groupId, pointId, item })

    // 查分组名
    const groupInfo = allGroups.value.find((g) => String(g.id) === groupId)
    const groupName = groupInfo?.groupName

    console.log('找到的分组信息:', groupInfo, '分组名:', groupName)

    // 查点位详情
    const pointInfo = allPoints.value.find((p) => p.id == pointId)
    const pointName = pointInfo?.pointName
    const pointIcon = pointInfo?.pointIcon

    console.log('找到的点位信息:', pointInfo, '点位名:', pointName)

    if (!groupsMap.has(groupId)) {
      groupsMap.set(groupId, {
        groupId,
        groupName,
        points: []
      })
    }

    const group = groupsMap.get(groupId)!

    // 检查是否重复
    const isDuplicate = group.points.some((p) => p.pointId === pointId)
    console.log('是否重复:', isDuplicate, '当前组内点位:', group.points)

    // 防止重复添加
    if (!isDuplicate) {
      group.points.push({
        pointId,
        pointName,
        pointIcon
      })
      console.log('添加点位后:', group.points)
    }
  })

  const result = Array.from(groupsMap.values())
  console.log('最终结果:', result)
  return result
})

// 获取某个分组下的隔离点
// const getPointsByGroupId = (groupId) => {
//   return ticketPointsList.value.filter((point) => point.groupId === groupId)
// }
// 从 JobForm.sopUserList 中提取锁定人并按 groupId 分组
const groupedLockers = computed(() => {
  const lockerUsers =
    JobForm.ticketUserList?.filter((u) => u.userRole === 'jtlocker' && u.groupId != null) || []
  const groupMap = new Map()

  lockerUsers.forEach((user) => {
    if (!groupMap.has(user.groupId)) {
      const groupName =
        groupList.value.find((g) => g.id === user.groupId)?.groupName || '未命名分组'
      groupMap.set(user.groupId, { groupId: user.groupId, groupName, users: [] })
    }
    groupMap.get(user.groupId).users.push(user)
  })

  return Array.from(groupMap.values())
})

// 提取共锁人
const coLockUsers = computed(() => {
  return JobForm.ticketUserList?.filter((u) => u.userRole === 'jtcolocker') || []
})

// 人员共锁
const updateUserGroups = () => {
  const allUsers = jobclockerList.value
  console.log(allUsers, jobclockerList.value, '数据拿到了吗')

  waitLockUsers.value = jobclockerList.value.filter(
    (u) => u.userRole == 'jtcolocker' && u.jobStatus == 0
  )
  console.log(waitLockUsers.value, '待共锁')
  lockedUsers.value = allUsers.filter((u) => u.userRole === 'jtcolocker' && u.jobStatus == 1)
  unlockPendingUsers.value = allUsers.filter((u) => u.userRole === 'jtcolocker' && u.jobStatus == 2)
}

const getWebSocket = async () => {
  const code = route.query.id
  const address = 'sys.websocket.address'
  const addressData = await getIsSystemAttributeByKey(address)
  const url = addressData.sysAttrValue
  const isLocalDev = window.location.hostname === 'localhost'
  const baseAddress = isLocalDev ? 'ws://192.168.0.10:48080' : url
  connectWebsocket(
    `${baseAddress}/websocket/jobTicketLog/${code}`,
    { w: 'S' },
    async (msg) => {
      console.log('接收消息:', msg)
      // const parts = msg.split('operationType=');
      if (msg !== 'heartbeat') {
        // 判断是否需要弹窗显示
        onNewSocketLog(msg) // 👈传入最新的 WebSocket 消息
        await getDetail() // 抽出专用接口函数
      }
      // 你的处理逻辑
    },
    (err) => {
      console.error('WebSocket 错误:', err)
    }
  )
}
//获取作业日志数据
// const getJobLogs = async () => {
//   const joblogData = {
//     titcketId: route.query.id,
//     current: 1,
//     size: -1
//   }
//   try {
//     const res = await JobApi.getTicketOperLogPage(joblogData)
//     joblogList.value = res.list
//     // ✨强制刷新筛选后的列表
//     filterJoblogs()
//     console.log(res.list, '日志数据', joblogList.value, '复制是否成功')
//   } catch (error) {
//     console.error('获取日志数据失败', error)
//   }
//
// }

// 作业日志页面数据展示样式分割
const renderLogContent = (content) => {
  if (!content) return ''
  // 第一步：先处理 <url>[label] 为 <img> + label
  content = content.replace(/<([^>]+)>\[([^\]]+)\]/g, (match, url, label) => {
    return `<img src="${url}" style="height: 25px; vertical-align: middle;"> <span style="color:#007BFF;margin: 0 3px">${label}</span>`
  })
  // 第二步：处理剩下的 [label]，转成蓝色字体
  content = content.replace(/\[([^\]]+)\]/g, '<span style="color:#007BFF;margin: 0 5px">$1</span>')
  return content
}

//   作业日志底部筛选功能
const handleCheckedJoblogsChange = (val) => {
  const checkedCount = val.length
  checkAlljoblog.value = checkedCount === joblogsOptions.value.length
  isIndeterminate.value = checkedCount > 0 && checkedCount < joblogsOptions.value.length

  filterJoblogs()
}
const handleCheckAllChange = (val) => {
  checkedJoblogs.value = val ? [...joblogsOptions.value] : []
  isIndeterminate.value = false
  filterJoblogs()
}

// 实际日志筛选逻辑
const filterJoblogs = () => {
  if (checkedJoblogs.value.includes('ALL')) {
    filteredJoblogList.value = joblogList.value
  } else {
    filteredJoblogList.value = joblogList.value.filter((log) =>
      checkedJoblogs.value.includes(String(log.operationType))
    )
  }
}
//   作业日志弹框
const parseLogString = (logStr) => {
  const obj = {}
  const match = logStr.match(/^[^(]+?\((.*)\)$/)
  if (!match) return obj
  const keyValuePairs = match[1].split(/,\s*(?=\w+=)/) // 处理 "key=value" 中的逗号
  keyValuePairs.forEach((pair) => {
    const [key, value] = pair.split('=')
    if (value === 'null') {
      obj[key] = null
    } else if (!isNaN(value)) {
      obj[key] = Number(value)
    } else {
      obj[key] = value
    }
  })

  return obj
}

// 判断某条日志是否在当前筛选之外（即是否未被选中）
const onNewSocketLog = async (rawLogStr) => {
  // 1. 将字符串解析为对象
  const newLog = parseLogString(rawLogStr)

  // 2. 获取当前勾选的类型（都转成字符串）
  const selectedTypes = checkedJoblogs.value.map(String)
  const newLogType = String(newLog.operationType)
  // console.log('新日志类型:', newLogType, '勾选类型:', selectedTypes);
  // 3. 判断是否未被选中 → 弹窗
  if (!selectedTypes.includes(newLogType)) {
    dialogLog.value = newLog
    console.log(isJoblogVisible.value, 'this.isJoblogVisible')
    if (isJoblogVisible.value) {
      JoblogDialogVisible.value = true
      console.log('弹框内容:', newLog)
    }
  } else {
    JoblogDialogVisible.value = false
    // console.log('该类型已勾选，不弹框');
  }
}
// 返回
const goBack = () => {
  router.push('/jobTicket/job')
}
</script>

<style scoped lang="scss">
.demo-tabs > .el-tabs__content {
  padding: 32px;
  color: #6b778c;
  font-size: 32px;
  font-weight: 600;
}

.basicInformation {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  margin-top: 20px;

  .basicContent {
    width: 100%;
    height: 100%;
    overflow-y: auto;
    display: flex;
    flex-wrap: wrap;
    padding: 10px 20px;
    box-sizing: border-box;
    //background: pink;
    p {
      display: block;
      min-width: 32%;
      min-height: 25px;
      margin: 8px 5px;
      //background: green;
      .specialText {
        color: #42bafa;
      }
    }
  }
}

.tab-header {
  background-color: #f5f7fa;
  border-bottom: 1px solid #dcdfe6;
  padding: 12px 20px;
  border-radius: 4px 4px 0 0;
  display: flex;
  align-items: center; /* 垂直居中 */
  .topTitle_left{
    width:95%;
  }
  .titleimg {
    width: 25px;
    height: 25px;
    margin-right: 8px;
  }

  .tab-title {
    font-size: 14px;
    font-weight: 500;
    color: #303133;
  }

  .set-btn {
    width: 60px;
    height: 30px;
    border: 1px solid black;
    border-radius: 6px;
    text-align: center;
    line-height: 30px;
    //float: right;
    cursor: pointer;
    margin: 10px 0;

    img {
      width: 14px;
      height: 14px;
    }
  }
}

.processDetail {
  width: 100%;
  height: 300px;
  overflow-x: auto;
  //background: green;
}

.jobExecution {
  width: 100%;
  height: 600px;
  //background: blue;
  .tab-label {
    padding: 12px 20px;
    border-radius: 4px 4px 0 0;
    display: flex;
    align-items: center; /* 垂直居中 */
    .titleimg {
      width: 25px;
      height: 25px;
      margin-right: 8px;
    }

    .tab-text {
      font-size: 14px;
      font-weight: 500;
      color: #303133;
    }
  }

  .jobMemberBox {
    width: 100%;
    min-height: 300px;
    display: flex;
    flex-wrap: wrap;
    //background: #000;
  }

  .left_box {
    width: 500px;
    margin-right: 10px;
    display: flex;
    flex-direction: column;

    img {
      width: 80px;
      height: 80px;
    }
  }

  .right_box {
    flex: 1;
    display: flex;
    flex-direction: column;

    img {
      width: 80px;
      height: 80px;
    }
  }

  //作业流程
  .custom-node {
    position: relative;
    width: 125px;
    height: 180px;
    //background-color: #fff;
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    padding: 10px;
    box-sizing: border-box;
  }

  .node-content {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
  }

  //连接点样式

  .handle {
    width: 12px;
    height: 12px;
    background-color: #1a192b;
    border: 2px solid #fff;
    border-radius: 50%;
    cursor: crosshair;
    position: absolute;
    z-index: 10;
  }

  .handle:hover {
    background-color: #555;
    transform: scale(1.2);
  }

  .handle-top {
    top: -8px;
    left: 50%;
    transform: translateX(-50%);
  }

  .handle-bottom {
    bottom: -8px;
    left: 50%;
    transform: translateX(-50%);
  }

  .handle-left {
    left: -8px;
    top: 50%;
    transform: translateY(-50%);
  }

  .handle-right {
    right: -8px;
    top: 50%;
    transform: translateY(-50%);
  }

  //连接点全局样式
  :deep(.vue-flow__handle) {
    width: 12px;
    height: 12px;
    background-color: #1a192b;
    border: 2px solid #fff;
    border-radius: 50%;
    cursor: crosshair;
  }

  :deep(.vue-flow__handle:hover) {
    background-color: #555;
    transform: scale(1.2);
  }

  //连接线样式
  :deep(.vue-flow__edge-path) {
    stroke: #333;
    stroke-width: 2;
  }

  :deep(.vue-flow__edge) {
    z-index: 1;
  }

  // 箭头样式
  :deep(.vue-flow__edge-marker) {
    fill: #333;
  }

  .group-container {
    display: flex;
    flex-wrap: wrap;
    gap: 20px; /* 卡片之间的间距 */
  }

  .point-group {
    border: 1px solid #ccc;
    border-radius: 8px;
    padding: 12px;
    min-width: 250px;
    background-color: #fafafa;
    height: 250px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
  }

  .group-title {
    font-weight: 600;
    font-size: 16px;
    margin-bottom: 12px;
    padding-bottom: 6px;
    border-bottom: 1px solid #e0e0e0;
    color: #333;
  }

  .points-list {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
  }

  .point-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 60px;
  }

  .point-icon {
    width: 40px;
    height: 40px;
    object-fit: contain;
  }

  .point-name {
    font-size: 12px;
    text-align: center;
    margin-top: 4px;
    color: #555;
  }

  //用户的卡片
  .group-container-user {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 16px;
    overflow-x: auto;
    padding-bottom: 10px;
  }

  .group-card-user {
    width: 180px;
    min-height: 150px;
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
    padding: 12px;
    //flex-shrink: 0;
    border: 1px solid #eee;
    display: flex;
    flex-direction: column;
    margin-top: 10px;
  }

  .group-title {
    font-weight: bold;
    font-size: 16px;
    margin-bottom: 10px;
    color: #333;
    text-align: center;
    border-bottom: 1px solid #f0f0f0;
    padding-bottom: 6px;
  }

  .user-list {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: center;
  }

  .user-list-colocker {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: flex-start;
    margin-top: 10px;
  }

  .user-card {
    width: 60px;
    text-align: center;

    img {
      width: 40px;
      height: 40px;
      border-radius: 4px;
      border: 1px solid #ccc;
    }

    .user-name {
      font-size: 12px;
      margin-top: 4px;
      color: #555;
      word-break: break-all;
    }
  }

  //点位数据
  .group-box {
    margin: 0 20px;
  }

  //人员共锁
  .biglockBox {
    width: 100%;
    height: 100%;
    display: flex;
    flex-wrap: wrap;
    //background: green;
    .mumberbox {
      width: 28%;
      height: 100%;
      margin: 0 15px;

      .user {
        width: 100%;
        max-height: 500px;
        overflow-y: auto;
        overflow-x: hidden;
        display: flex;
        flex-wrap: wrap;
        padding: 2px 5px;
        box-sizing: border-box;
        //background: pink;
        .userItem {
          width: 20%;
          height: 80px;
          padding: 2px 3px;
          margin: 10px;
          text-align: center;
          //background: #000;
          box-sizing: border-box;

          img {
            width: 50px;
            height: 50px;
          }

          p {
            font-size: 16px;
            display: -webkit-box;
            -webkit-line-clamp: 1; /* 最多显示1行 */
            -webkit-box-orient: vertical;
            overflow: hidden;
            text-overflow: ellipsis;
          }
        }
      }
    }

    .arrow {
      margin-top: 15%;
    }
  }

  //作业日志
  .joblogCon {
    width: 98%;
    height: 75%;
    margin: auto;
    //background: greenyellow;
    .joblogTop {
      width: 100%;
      height: 490px;
      margin: auto;
      overflow-y: auto;
      //background: cadetblue;
      p {
        font-size: 16px;
        line-height: 20px;
        margin: 10px 0;
      }
    }

    .bottomCheck {
      width: 100%;
      min-height: 50px;
      line-height: 50px;
      font-size: 23px;
      display: flex;
      padding: 5px 0;
      margin: auto;
      //background: forestgreen;

      .big-checkbox {
        font-size: 23px; /* 字体大小 */
      }
    }
  }

  /* 小屏幕下隐藏文字，只显示图标 */
  @media (max-width: 768px) {
    .tab-label {
      padding: 12px 2px;
      border-radius: 4px 4px 0 0;
      display: flex;
      align-items: center; /* 垂直居中 */
      .titleimg {
        width: 25px;
        height: 25px;
        margin-right: 0;
      }

      .tab-text {
        font-size: 14px;
        font-weight: 500;
        color: #303133;
        display: none;
      }
    }
    .group-card-user {
      width: 160px;
      min-height: 130px;
    }
    //点位数据
    .group-box {
      margin: 10px 5px;
    }
    //  人员共锁
    .biglockBox {
      overflow-y: auto;
      flex-direction: column;
      align-items: center;
      //background: #bf2020;
      .mumberbox {
        width: 95%;
        margin: 15px 0;
      }

      .arrow {
        transform: rotate(90deg); // 向右 → 向下
        margin-top: 10px;
        margin-bottom: 10px;
      }
    }
  }
}
</style>
