<template>
  <!-- 列表卡片模式 listMode: card，高度由 CSS 变量 --x-list-card-height 控制，默认来自配置 cardHeight -->
  <div class="x-list-wrapper" :class="wrapperClassObject" :style="cardModeWrapperStyle" v-if="listMode" ref="listRef" @scroll="handleInfiniteOnLoad">
    <a-list :grid="listGrid" :data-source="localData">
      <a-list-item slot="renderItem" slot-scope="item, index">
        <div
          class="card-a-col"
          :class="{ 'selected-active': enableSelectRow && currentSelectedIndex === index }"
          :style="getCardStyle(currentTitleValue(index))"
          @click="handleCardClick(index)"
        >
          <a-row class="card-row">
            <a-col v-if="showIdColumn" class="id-a-col" :span="4" v-for="(detail, idx) in item.filter(d => d.label == label)" :key="idx">
              {{ detail.value }}
              <div class="gender-icon" v-if="getHasPatient(item) && getGender(item)">
                <img
                  :src="getGender(item) === '男' ? maleIcon : femaleIcon"
                  :alt="getGender(item)"
                  class="gender-img"
                />
              </div>
            </a-col>
            <a-col :span="showIdColumn ? 20 : 24" class="id-a-col-2">
              <template v-for="(detail, idx) in item">
                <div :key="`title_${idx}`" class="title-row" v-if="detail.type == 'title'">
                  <a-tooltip :title="detail.value" placement="topLeft">
                    <span class="describe-list-a-col" :class="{ name: detail.bold }">{{ detail.value }}</span>
                  </a-tooltip>
                  <div class="title-actions" v-if="getTitleOptions && getTitleOptions.length">
                    <span class="title-select-label">{{ getDisplayTitleLabel(index) }}</span>
                    <a-dropdown placement="bottomRight" trigger="['click']">
                      <a class="arrow-btn" @click.prevent>
                        <a-icon type="down" />
                      </a>
                      <a-menu
                        slot="overlay"
                        :selectedKeys="[String(currentTitleValue(index))]"
                        @click="info => handleTitleMenuClick(info, index)"
                      >
                        <a-menu-item
                          v-for="opt in getTitleOptions"
                          :key="String(opt && opt.value !== undefined ? opt.value : opt)"
                        >
                          {{ opt && opt.label !== undefined ? opt.label : opt }}
                        </a-menu-item>
                      </a-menu>
                    </a-dropdown>
                  </div>
                </div>
                <span
                  :key="idx"
                  class="component-a-col"
                  v-else-if="getHasPatient(item) && detail.type == 'component' && detail.label != label"
                >
                  <a-tooltip :title="detail.label" placement="topLeft">
                    <span class="label-text">{{ `${detail.label}：` }}</span>
                  </a-tooltip>
                  <component
                    :is="detail.slotType"
                    :key="idx"
                    :ref="`dynamicComponent_${idx.slotType || idx}_${idx}`"
                    :serviceName="serviceName"
                    v-on="forwardAllEvents"
                    :queryParamsName="detail.value"
                    :countVisible="false"
                  />
                </span>
                <span :key="`${idx}-date`" class="component-a-col" v-else-if="getHasPatient(item) && detail.type == 'date'">
                  <a-tooltip :title="detail.label" placement="topLeft">
                    <span class="label-text">{{ `${detail.label}：` }}</span>
                  </a-tooltip>
                  <a-date-picker @change="onChange" />
                </span>
                <!-- Option 区块：直接传数据对象；字段名由外部配置 JS 在每条数据上设置（questionLabelField/optionsField/multipleField/selectedField），选择后写入该对象的 selectedField 字段 -->
                <XQuestionOptions
                  :key="`options_${index}_${idx}`"
                  v-else-if="detail.type == 'options'"
                  :question-data="detail"
                  :question-label-field="detail.questionLabelField"
                  :options-field="detail.optionsField"
                  :multiple-field="detail.multipleField"
                  :selected-field="detail.selectedField"
                  :name="'opt_'+index+'_'+idx"
                  :value="getDetailSelected(detail)"
                  @change="payload => $emit('optionChange', payload)"
                />
                <a-tooltip
                  :key="`${idx}-tooltip`"
                  :title="`${detail.label}：${detail.value}`"
                  placement="topLeft"
                  v-else-if="getHasPatient(item) && detail.label != label"
                >
                  <span class="describe-list-a-col" :class="{ name: detail.bold }">
                    {{ `${detail.label}：${detail.value}` }}
                  </span>
                </a-tooltip>
              </template>
              <a-button
                v-if="getHasPatient(item) && showCardButtons"
                v-for="(btn, i) in buttonNames"
                :key="i"
                icon="search"
                class="button-a-col"
                @click.stop="click(item, index)"
              >
                {{ btn }}
              </a-button>
            </a-col>
          </a-row>
        </div>
      </a-list-item>
      <div v-if="loading" class="demo-loading-container">
        <a-spin />
      </div>
      <div v-if="allLoaded">
        <div class="demo-infinite-list-bottom">已经显示全部数据</div>
      </div>
    </a-list>
  </div>

  <!-- 默认标签模式 -->
  <div class="list-wrapper x-list-wrapper" :class="wrapperClassObject" v-else>
    <a-list size="large" :data-source="data" itemLayout="horizontal" class="list-container" ref="listRef">
      <a-list-item
        slot="renderItem"
        slot-scope="item, index"
        class="list-item"
        @click="handleClick(index)"
        @mouseenter="effectiveEnableHoverOptions && handleMouseEnter(index)"
        @mouseleave="handleMouseLeave"
        :class="{
          'hover-active': effectiveEnableHoverOptions && hoveredIndex === index,
          'selected-active': enableSelectRow && currentSelectedIndex === index
        }"
      >
        <i v-if="icon" class="icon-menu" :style="getIconStyle(item)"></i>
        <span
          class="item-text"
          :class="{ 'text-truncated': effectiveEnableHoverOptions && hoveredIndex === index }"
          :style="getTextStyle(index)"
        >
          {{ getListItemNumber(item, index) }} {{ getListItemName(item) }}
        </span>

        <div v-if="button" class="button-group">
          <a-button
            v-for="(name, idx) in buttonNames"
            :key="idx"
            type="link"
            :class="['confirm-btn', buttonMode ? 'hover-btn' : '']"
            @click.stop="click(index, idx)"
          >
            <span :class="{ 'hover-active': effectiveEnableHoverOptions && hoveredIndex === index }">{{ name }}</span>
          </a-button>
        </div>

        <!-- 悬浮选项框 -->
        <div
          v-show="effectiveEnableHoverOptions && hoveredIndex === index"
          class="hover-options"
          @mouseenter="handleOptionsEnter"
          @mouseleave="handleOptionsLeave"
        >
          <div class="hover-options-content">
            <div
              v-for="(Item, idx) in select_options"
              :key="idx"
              class="option-item"
              @click="handleOptionClick(index, Item)"
            >
              {{ Item }}
            </div>
          </div>
        </div>
      </a-list-item>
    </a-list>
  </div>
</template>

<script>
import { getConfigByName, runLogic } from '@vue2-client/services/api/common'

export default {
  name: 'XList',
  components: {
    XReport: () => import('@vue2-client/base-client/components/common/XReport/XReport.vue'),
    XButtons: () => import('@vue2-client/base-client/components/common/XButtons/XButtons.vue'),
    XInput: () => import('@vue2-client/base-client/components/common/XInput/XInput.vue'),
    XRadio: () => import('@vue2-client/base-client/components/his/XRadio/XRadio.vue'),
    XTimeSelect: () => import('@vue2-client/base-client/components/his/XTimeSelect/XTimeSelect.vue'),
    XCheckbox: () => import('@vue2-client/base-client/components/his/XCheckbox/XCheckbox.vue'),
    XTitle: () => import('@vue2-client/base-client/components/his/XTitle/XTitle.vue'),
    XSelect: () => import('@vue2-client/base-client/components/his/XSelect/XSelect.vue'),
    XQuestionOptions: () => import('@vue2-client/base-client/components/his/XQuestionOptions/XQuestionOptions.vue')
  },
  props: {
    // 配置名（字符串）或配置对象；与 XTable 等一致，常为字符串如 "aiConsultationSuggestionListConfig"
    queryParamsName: {
      type: [String, Object],
      default: null
    },
    fixedQueryForm: {
      type: Object,
      default: () => {
        return { condition: '1=1' }
      }
    },
    enableHoverOptions: {
      type: Boolean,
      default: true
    },
    serviceName: {
      type: String,
      default: 'af-his'
    },
    // 点击是否触发选中（默认不通过点击选中，仅手动控制）
    selectOnClick: {
      type: Boolean,
      default: false
    },
    // 受控选中索引；不传则内部维护
    selectedIndex: {
      type: Number,
      default: undefined
    },
    // 卡片按钮显示控制（默认隐藏，可通过属性或配置开启）
    showCardButtons: {
      type: Boolean,
      default: false
    },
    // 标题右侧下拉选项与受控值
    titleSelectOptions: {
      type: Array,
      default: () => []
    },
    titleSelectValue: {
      type: [String, Number],
      default: undefined
    },
    /**
     * 直接传入的列表数据（不经过 runLogic）。
     * 当提供时，仍通过 queryParamsName 调用 getConfigByName 获取配置（listMode、label 等），
     * 但数据使用 initialData 而非 runLogic 结果。
     * 格式与 listMode 一致：默认模式 [{ number, name }, ...]，卡片模式二维数组。
     */
    initialData: {
      type: Array,
      default: null
    }
  },
  data() {
    return {
      data: [], // 数据源
      localData: [], // 本地数据源
      loading: false, // 加载中
      busy: false, // 繁忙状态
      button: false,
      icon: false,
      buttonNames: [],
      listMode: undefined, // 列表模式
      // 卡片栅格：默认每行多列；可通过配置 grid 覆盖，如 { xs:1, sm:1, md:1, lg:1, xl:1, xxl:1 } 每行 1 个
      listGrid: { gutter: 16, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 4 },
      // 卡片高度，由配置 cardHeight 指定，供 CSS 变量 --x-list-card-height 使用，默认 297px
      listCardHeight: '297px',
      // 是否显示左侧序号列（label 匹配的列）；配置 showIdColumn: false 可隐藏
      showIdColumn: true,
      buttonMode: true,
      hoveredIndex: -1, // 当前悬浮的索引
      isOptionsHovered: false, // 悬浮选项框是否悬浮
      hoverTimer: null, // 悬浮选项框定时器
      leaveTimer: null, // 离开选项框定时器
      select_options: [], // 悬浮选项框
      logicName: '',
      nowPage: 0, // 当前页
      pageSize: 12,
      allLoaded: false,
      label: 'id',
      // 默认列表模式字段映射（可通过配置覆盖）
      numberField: 'number',
      nameField: 'name',
      scrollTimer: null,
      // 内部选中索引（非受控时使用）
      localSelectedIndex: -1,
      maleIcon: require('@vue2-client/assets/svg/male.svg'),
      femaleIcon: require('@vue2-client/assets/svg/female.svg'),
      // 下拉配置
      internalTitleOptions: [],
      titleValueByIndex: {},
      // 配置加载后的悬浮选项开关（不直接改 prop）
      internalEnableHoverOptions: undefined,
      // 选择请求的轻量防抖
      selectDebounceTimer: null,
    }
  },
  inject: ['getComponentByName'],
  emits: ['init'],
  created() {
    this.getData(this.queryParamsName, this.fixedQueryForm)
  },
  mounted() {
    this.$emit('init', this)
  },
  computed: {
    // 参考 HForm 的 wrapperClassObject 规则，支持通过组件属性动态控制样式
    wrapperClassObject() {
      const a = this.$attrs || {}
      const classes = {}
      // 多个布尔型样式开关（存在且为真则生效）
      const booleanStyleKeys = ['enableNewListStyle']
      booleanStyleKeys.forEach(key => {
        const val = a[key]
        const truthy = val === true || val === '' || val === 'true'
        if (truthy) classes[`x-list-${key}`] = true
      })
      return classes
    },
    // 标题下拉：优先使用外部 props，否则使用内部配置
    getTitleOptions() {
      if (Array.isArray(this.titleSelectOptions) && this.titleSelectOptions.length) return this.titleSelectOptions
      return this.internalTitleOptions
    },
    currentTitleValue() {
      return index => {
        if (this.titleSelectValue !== undefined) return this.titleSelectValue
        const local = this.titleValueByIndex[index]
        if (local !== undefined) return local
        // 二次回退：若模板标题提供了 titleRightValue，则用于首屏渲染颜色与文案
        const row = Array.isArray(this.localData) ? this.localData[index] : null
        if (Array.isArray(row)) {
          const title = row.find(d => d && d.type === 'title')
          if (title && title.titleRightValue !== undefined) return title.titleRightValue
        }
        // 最终回退：使用 options 的第一个
        const def =
          this.internalTitleOptions && this.internalTitleOptions.length
            ? this.internalTitleOptions[0].value !== undefined
              ? this.internalTitleOptions[0].value
              : this.internalTitleOptions[0]
            : undefined
        return def
      }
    },
    // 选择控制：受控优先
    currentSelectedIndex() {
      return typeof this.selectedIndex === 'number' ? this.selectedIndex : this.localSelectedIndex
    },
    // 悬浮选项：优先使用配置加载结果，否则用 prop
    effectiveEnableHoverOptions() {
      return this.internalEnableHoverOptions !== undefined && this.internalEnableHoverOptions !== null
        ? this.internalEnableHoverOptions
        : this.enableHoverOptions
    },
    enableSelectRow() {
      const a = this.$attrs || {}
      const val = a.enableSelection
      return val === true || val === '' || val === 'true'
    },
    forwardAllEvents() {
      return {
        // 监听所有事件并转发给父组件
        '*': (eventName, ...payload) => {
          this.$emit(eventName, ...payload)
        }
      }
    },
    // 卡片模式下挂载 CSS 变量 --x-list-card-height，供 .card-a-col 使用
    cardModeWrapperStyle() {
      if (!this.listMode) return undefined
      return { '--x-list-card-height': this.listCardHeight }
    }
  },
  methods: {
    onChange(date, dateString) {
      this.$emit('dateChange', date, dateString)
    },
    handleInfiniteOnLoad(event) {
      if (this.busy || this.allLoaded) return // 防止重复加载
      if (!this.logicName) return // 未配置 data 时不请求
      if (this.scrollTimer) clearTimeout(this.scrollTimer)
      this.scrollTimer = setTimeout(() => {
        const container = event.target
        const scrollTop = container.scrollTop
        const scrollHeight = container.scrollHeight
        const clientHeight = container.clientHeight
        const bottomOffset = 10
        if (scrollTop + clientHeight >= scrollHeight - bottomOffset) {
          this.busy = true
          this.loading = true
          this.nowPage = this.nowPage + this.pageSize
          this.fixedQueryForm.condition = `Limit ${this.nowPage}, ${this.pageSize}`
          runLogic(this.logicName, this.fixedQueryForm, 'af-his')
            .then(async res => {
              this.localData = [...this.localData, ...res]
              if (res.length < this.pageSize) {
                this.allLoaded = true
              }
            })
            .catch(e => {
              this.$message.error(e.message)
            })
            .finally(() => {
              this.loading = false
              this.busy = false
            })
        }
      }, 100)
    },
    async getData(config, param) {
      const that = this
      this._configEndEmitted = false
      this._configEndPromise = new Promise(resolve => { this._configEndResolve = resolve })
      const applyResult = result => {
        const list = Array.isArray(result) ? result : []
        that.data = list
        if (that.nowPage === 0) {
          that.localData = list
        }
        if (Array.isArray(that.localData)) {
          that.localData.forEach((row, idx) => {
            if (that.titleValueByIndex[idx] !== undefined) return
            if (Array.isArray(row)) {
              const title = row.find(d => d && d.type === 'title')
              if (title && title.titleRightValue !== undefined) {
                that.$set(that.titleValueByIndex, idx, title.titleRightValue)
                return
              }
            }
            const def =
              that.internalTitleOptions && that.internalTitleOptions.length
                ? that.internalTitleOptions[0].value !== undefined
                  ? that.internalTitleOptions[0].value
                  : that.internalTitleOptions[0]
                : undefined
            if (def !== undefined) that.$set(that.titleValueByIndex, idx, def)
          })
        }
      }
      const processConfig = res => {
        that.listMode = res.listMode == 'card'
        that.logicName = res.data
        that.button = res.button // 按钮
        that.icon = res.icon //  图标
        that.label = res.label // 标签
        // 默认列表模式字段映射：允许把 number/name 映射到任意字段（如 id/name）
        that.numberField = res.numberField || 'number'
        that.nameField = res.nameField || 'name'
        that.buttonNames = (res.buttonNames) || [] // 按钮文本
        that.buttonMode = (res.buttonMode) || false // 按钮模式
        that.cardButtonsVisible = (res.cardButtonsVisible) || false // 卡片按钮
        const grid = res.grid
        if (grid && typeof grid === 'object') {
          that.listGrid = { gutter: 16, ...grid }
        }
        const cardH = res.cardHeight
        if (cardH != null) {
          that.listCardHeight = typeof cardH === 'number' ? cardH + 'px' : String(cardH)
        }
        if (res.showIdColumn === false) that.showIdColumn = false
        if (Array.isArray(res.titleOptions)) {
          this.internalTitleOptions = res.titleOptions
          if (res.titleDefaultValue !== undefined && Array.isArray(this.localData)) {
            this.titleValueByIndex = {}
          }
        }
        const enableHover = (res.enableHoverOptions) || false // 悬浮选项框
        this.internalEnableHoverOptions = enableHover
        if (enableHover) {
          this.select_options = res.select_options
        }
        if (that.initialData != null && Array.isArray(that.initialData)) {
          applyResult(that.initialData)
          that._emitConfigEnd()
        } else {
          if (that.listMode) {
            param = param || {}
            param.condition = `Limit ${that.nowPage}, ${that.pageSize}`
          }
          runLogic(res.data, param, 'af-his').then(result => {
            applyResult(result)
            that._emitConfigEnd()
          })
        }
      }
      if (!config && that.initialData != null && Array.isArray(that.initialData)) {
        processConfig({ listMode: false, label: 'id' })
      } else {
        getConfigByName(config, 'af-his', processConfig)
      }
    },
    /** 子组件配置加载结束后发出，供 XReport 判断「所有 slot 配置结束」；仅发出一次 */
    _emitConfigEnd() {
      if (this._configEndEmitted) return
      this._configEndEmitted = true
      if (this._configEndResolve) {
        this._configEndResolve()
        this._configEndResolve = null
      }
      this.$emit('configEnd')
    },
    /**
     * 等待配置结束的异步方法。配置已结束则立即 resolve，否则在 configEnd 时 resolve。
     * @returns {Promise<void>}
     */
    waitConfigEnd() {
      if (this._configEndEmitted) return Promise.resolve()
      return this._configEndPromise || Promise.resolve()
    },
    // 提取性别：从数组数据中查找 label 含“性别”的项
    getGender(item) {
      if (!Array.isArray(item)) return null
      const g = item.find(d => d && typeof d.label === 'string' && d.label.indexOf('性别') > -1)
      const val = g && (g.value || g.text)
      if (!val) return null
      if (String(val).includes('男')) return '男'
      if (String(val).includes('女')) return '女'
      return null
    },
    // 读取标题项上的 hasPatient 布尔值
    getHasPatient(item) {
      if (!Array.isArray(item)) return true
      const title = item.find(d => d && d.type === 'title')
      if (!title || typeof title.hasPatient === 'undefined') return true
      return !!title.hasPatient
    },
    /** 从某条 options 单元格上按 selectedField 读取当前选中值（供 :value 绑定） */
    getDetailSelected(detail) {
      if (!detail || detail.selectedField == null) return undefined
      return detail[detail.selectedField]
    },
    /**
     * 对外获取所有选项区块（即原始问题对象）；选择结果已写在各自对象的 selectedField 指定字段上。
     * @returns {Array<Object>} 每个元素为 type==='options' 的 detail 对象
     */
    getOptionValues() {
      const out = []
      if (!Array.isArray(this.localData)) return out
      this.localData.forEach(row => {
        if (!Array.isArray(row)) return
        row.forEach(detail => {
          if (detail && detail.type === 'options') out.push(detail)
        })
      })
      return out
    },
    // 标题右侧下拉选择
    handleTitleSelectChange(val, index) {
      // 重复值拦截：若值未变化则直接返回
      const prev = this.normalizeValue(this.currentTitleValue(index))
      const next = this.normalizeValue(val)
      if (prev === next) return

      this.$set(this.titleValueByIndex, index, val)
      this.$emit('update:titleSelectValue', val)
      this.$emit('titleSelectChange', { index, value: val })
      // 变更后仅通知后端更新，不处理返回值；支持从标题项读取 extraParams 合并（如住院号）
      const extra = this.getTitleExtraParams(index)
      if (this.selectDebounceTimer) clearTimeout(this.selectDebounceTimer)
      this.selectDebounceTimer = setTimeout(() => {
        if (!this.logicName) return
        runLogic(this.logicName, Object.assign({ type: 'select', value: val }, extra), 'af-his').catch(() => {})
      }, 200)
    },
    // 读取标题项中额外需要传递的参数（可选），例如 { inpatientNo: row.t_id }
    getTitleExtraParams(index) {
      const row = Array.isArray(this.localData) ? this.localData[index] : null
      if (!Array.isArray(row)) return {}
      const title = row.find(d => d && d.type === 'title') || {}
      if (title && title.extraParams && typeof title.extraParams === 'object') return title.extraParams
      return {}
    },
    handleTitleMenuClick({ key }, index) {
      const v = isNaN(Number(key)) ? key : Number(key)
      this.handleTitleSelectChange(v, index)
    },
    // 根据当前选择项的 color 设置卡片背景色
    normalizeValue(v) {
      return String(v)
    },
    getCardStyle(val) {
      const target = this.normalizeValue(val)
      const opt = (this.getTitleOptions || []).find(
        o => this.normalizeValue(o && (o.value !== undefined ? o.value : o)) === target
      )
      const color = opt && (opt.color || (opt.style && opt.style.background))
      return color ? { backgroundColor: color } : {}
    },
    getTitleLabel(val) {
      const target = this.normalizeValue(val)
      const opt = (this.getTitleOptions || []).find(
        o => this.normalizeValue(o && (o.value !== undefined ? o.value : o)) === target
      )
      return opt && (opt.label !== undefined ? opt.label : opt)
    },
    getDisplayTitleLabel(index) {
      // 优先展示“当前选中值”的 label，未选中再回退到模板提供的 titleRightLabel
      const current = this.getTitleLabel(this.currentTitleValue(index))
      if (current) return current
      const title = Array.isArray(this.localData && this.localData[index])
        ? this.localData[index].find(d => d && d.type === 'title')
        : null
      return title && title.titleRightLabel ? title.titleRightLabel : ''
    },
    // 点击列表项
    handleClick(index) {
      if (this.enableSelectRow && this.selectOnClick) this.setSelectedIndex(index)
      this.$emit('listClick', this.data[index])
    },
    // 卡片模式点击卡片
    handleCardClick(index) {
      if (this.enableSelectRow && this.selectOnClick) this.setSelectedIndex(index)
      this.$emit('listClick', this.localData[index])
    },
    // 外部可调用：设置选中索引
    setSelectedIndex(index) {
      const next = typeof index === 'number' ? index : -1
      if (typeof this.selectedIndex === 'number') {
        // 受控：仅派发事件
        this.$emit('update:selectedIndex', next)
      } else {
        // 非受控：更新内部并派发事件
        this.localSelectedIndex = next
        this.$emit('update:selectedIndex', next)
      }
    },
    // 外部可调用：清空选中
    clearSelected() {
      this.setSelectedIndex(-1)
    },
    refreshList(param) {
      this.getData(this.queryParamsName, param)
    },
    /**
     * 外部可调用：直接设置列表数据（不经过 data 配置 / runLogic）。
     * @param {Array} list - 列表数据，格式与当前 listMode 一致：默认模式 [{ number, name }, ...]，卡片模式二维数组
     */
    setData(list) {
      const arr = Array.isArray(list) ? list : []
      this.data = arr
      this.localData = arr
      if (Array.isArray(this.localData)) {
        this.localData.forEach((row, idx) => {
          if (this.titleValueByIndex[idx] !== undefined) return
          if (Array.isArray(row)) {
            const title = row.find(d => d && d.type === 'title')
            if (title && title.titleRightValue !== undefined) {
              this.$set(this.titleValueByIndex, idx, title.titleRightValue)
              return
            }
          }
          const def =
            this.internalTitleOptions && this.internalTitleOptions.length
              ? this.internalTitleOptions[0].value !== undefined
                ? this.internalTitleOptions[0].value
                : this.internalTitleOptions[0]
              : undefined
          if (def !== undefined) this.$set(this.titleValueByIndex, idx, def)
        })
      }
    },
    click(index, buttonIndex) {
      this.$emit('click', { data: this.data[index], name: this.buttonNames[buttonIndex] })
    },
    // 根据对象字段匹配选中（默认按 id 字段）
    setSelectedById(id, field = 'id') {
      const list = this.listMode ? this.localData : this.data
      if (!Array.isArray(list)) return
      const index = list.findIndex(item => {
        if (Array.isArray(item)) {
          const f = item.find(d => d && d.label === field)
          return f && f.value === id
        }
        return item && item[field] === id
      })
      if (index >= 0) this.setSelectedIndex(index)
    },
    // 根据 label/value（卡片数组数据场景）匹配选中
    setSelectedByLabelValue(label, value) {
      const list = this.listMode ? this.localData : this.data
      if (!Array.isArray(list)) return
      const index = list.findIndex(
        item => Array.isArray(item) && item.some(d => d && d.label === label && d.value === value)
      )
      if (index >= 0) this.setSelectedIndex(index)
    },
    // 通用：传入谓词函数决定选中项
    setSelectedBy(predicate) {
      if (typeof predicate !== 'function') return
      const list = this.listMode ? this.localData : this.data
      if (!Array.isArray(list)) return
      const index = list.findIndex(predicate)
      if (index >= 0) this.setSelectedIndex(index)
    },
    getIconStyle(item) {
      return item.picture ? { backgroundImage: `url(${item.picture})` } : {}
    },
    filterData(par) {
      runLogic(this.queryParamsName, par, 'af-his').then(res => {
        this.data = res.data
      })
    },
    // 默认列表模式：序号字段与名称字段可配置，保证 JSON 注入数据（如 {id,name}）也能正确展示
    getListItemNumber(item, index) {
      if (!item || typeof item !== 'object') return index + 1
      const v = item[this.numberField]
      // 允许 number/id 为 0；仅在 null/undefined/'' 时回退到 index+1
      return v === null || v === undefined || v === '' ? index + 1 : v
    },
    getListItemName(item) {
      if (!item || typeof item !== 'object') return ''
      const v = item[this.nameField]
      return v === null || v === undefined ? '' : v
    },
    // 鼠标进入列表项
    handleMouseEnter(index) {
      this.clearAllTimers()
      this.hoveredIndex = index
      this.isOptionsHovered = true
    },
    // 鼠标离开列表项
    handleMouseLeave() {
      this.clearAllTimers()
      this.leaveTimer = setTimeout(() => {
        this.isOptionsHovered = false
        this.hoveredIndex = -1
      }, 100)
    },
    // 鼠标进入悬浮选项框
    handleOptionsEnter() {
      this.clearAllTimers()
      this.isOptionsHovered = true
    },
    // 鼠标离开悬浮选项框
    handleOptionsLeave() {
      this.clearAllTimers()
      this.leaveTimer = setTimeout(() => {
        this.isOptionsHovered = false
        this.hoveredIndex = -1
      }, 100)
    },
    // 清除所有定时器
    clearAllTimers() {
      if (this.hoverTimer) {
        clearTimeout(this.hoverTimer)
        this.hoverTimer = null
      }
      if (this.leaveTimer) {
        clearTimeout(this.leaveTimer)
        this.leaveTimer = null
      }
    },
    // 选项框点击
    handleOptionClick(index, action) {
      this.$emit('optionClick', { data: this.data[index], action })
    },
    // 获取文本样式（简化版本，主要依赖CSS类控制）
    getTextStyle(index) {
      return {}
    }
  },
  watch: {
    // 同步受控值变化
    selectedIndex(val) {
      if (typeof val === 'number') {
        // 允许受控值影响内部显示
        this.localSelectedIndex = val
      }
    },
    fixedQueryForm: {
      deep: true,
      handler(val) {
        this.refreshList(val)
      }
    }
  },
  beforeDestroy() {
    const ref = this.$refs.listRef
    if (ref && ref.removeEventListener) {
      ref.removeEventListener('scroll', this.handleInfiniteOnLoad)
    }
    this.clearAllTimers()
  }
}
</script>

<style scoped lang="less">
.list-wrapper {
  max-height: 240px;
  overflow-y: auto;
  padding-right: 2px;
}

.list-container {
  width: 100%;
}

.list-item {
  height: 35px;
  border-radius: 6px;
  background-color: #f4f4f4;
  padding: 8px 15px;
  font-size: 16px;
  display: flex;
  align-items: center;
  width: 100%;
  border: 1px solid #d9d9d9;
  box-sizing: border-box;
  margin-bottom: 8px !important;
  position: relative;
  transition: background-color 0.3s ease;
}

.icon-menu {
  display: inline-block;
  width: 20px;
  height: 20px;
  background-color: #ccc;
  margin-right: 8px;
}

.item-text {
  flex: 1;
}

.confirm-btn {
  margin-left: auto;
  padding: 0 8px;
}

.confirm-btn.hover-btn {
  opacity: 0;
  transition: opacity 0.3s ease;
}

.button-group {
  display: flex;
  gap: 4px; /* 按钮之间的间距 */
}

.list-item:hover .confirm-btn.hover-btn {
  opacity: 1;
}

/* 自定义滚动条样式 */
.list-wrapper::-webkit-scrollbar {
  width: 6px;
}

.list-wrapper::-webkit-scrollbar-thumb {
  background-color: #d9d9d9;
  border-radius: 3px;
}

.list-wrapper::-webkit-scrollbar-track {
  background-color: #f0f0f0;
}

.hover-active {
  color: white;
}

.list-item.hover-active {
  background-color: rgb(0, 87, 254) !important;
  color: white;
  border: 1px solid black;
}

/* 选中态样式（通过 selectRow 开启） */
.selected-active {
  color: white;
}
.list-item.selected-active {
  background-color: #0057fe !important;
  color: white;
  border: none !important;
}
.list-item.selected-active .confirm-btn,
.list-item.selected-active .confirm-btn span,
.list-item.selected-active .ant-btn,
.list-item.selected-active .ant-btn > span {
  color: #ffffff !important;
}
.card-a-col.selected-active {
  background-color: #0057fe !important;
  color: white;
  border: none !important;
}
.card-a-col.selected-active .button-a-col,
.card-a-col.selected-active .button-a-col .ant-btn,
.card-a-col.selected-active .button-a-col .ant-btn > span {
  color: #ffffff !important;
}

.hover-options {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: white;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  z-index: 1000;
  margin-top: 4px;
  width: 100%;
  box-sizing: border-box;
  pointer-events: auto;
}

.hover-options-content {
  padding: 4px 0;
  display: flex;
  flex-direction: column;
  width: 100%;
}

.option-item {
  padding: 8px 12px;
  cursor: pointer;
  transition: all 0.3s ease;
  color: #333;
  font-size: 14px;
  display: flex;
  align-items: center;
}

.option-item:hover {
  background-color: #f5f5f5;
  color: #1890ff;
}

.option-item:active {
  background-color: #e6f7ff;
}

.demo-loading-container {
  position: absolute;
  bottom: 40px;
  width: 100%;
  text-align: center;
}

.demo-infinite-container {
  overflow-x: hidden;
  overflow-y: auto;
  height: 85vh;
}
.card-row {
  height: 100%;
  width: 100%;
  border-radius: 6px;
  padding: 6px;
}
.id-a-col {
  font-size: 22px;
  font-weight: 700;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.id-a-col-2 {
  padding: 8px;
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.describe-list-a-col {
  display: block;
  font-family: Source Han Sans;
  font-size: 16px;
  font-weight: normal;
  line-height: normal;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.name {
  font-family: Source Han Sans;
  font-size: 22px;
  font-weight: bold;
  line-height: normal;
}
.component-a-col {
  display: flex;
  align-items: center;
  overflow: hidden;
}
.label-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 120px;
  display: inline-block;
}
.gender-icon {
  margin-top: 2px;
}
.gender-icon .male {
  color: #2f54eb;
  font-size: 14px;
}
.gender-icon .female {
  color: #eb2f96;
  font-size: 14px;
}
.gender-img {
  width: 100%;
  height: 100%;
  opacity: 1;
  display: inline-block;
}
.title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
/* 标题文本占满剩余空间，确保箭头贴右 */
.title-row .describe-list-a-col {
  flex: 1;
}
.title-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.title-select-label {
  font-family: Source Han Sans;
  font-size: 16px;
  font-weight: normal;
  line-height: normal;
  color: #595959;
}
.title-select {
  min-width: 96px;
}
.arrow-btn {
  display: inline-flex;
  align-items: center;
  color: #8c8c8c;
}
.arrow-btn .anticon {
  font-size: 18px;
}
/* 放大下拉箭头的 svg 尺寸以符合 UI（scoped 深度选择） */
::v-deep .arrow-btn .anticon svg {
  width: 2em;
  height: 2em;
}
.button-a-col {
  position: absolute;
  top: 245px;
  left: 225px;
}
.card-a-col {
  background-color: rgba(247, 249, 252);
  height: var(--x-list-card-height, 297px);
  width: auto;
  border-radius: 6px;
  border: 1px solid #e5e9f0;
  overflow: hidden;
  position: relative;
  box-sizing: border-box;
  margin: 10px;
}

/* 新列表样式 */
.x-list-enableNewListStyle {
  /* 重写 list-wrapper 样式，去除 padding-right、max-height 和滚动条 */
  &.x-list-wrapper.list-wrapper {
    padding-right: 0 !important;
    max-height: none !important;
    overflow: visible !important;
    overflow-y: visible !important;
  }

  .list-item {
    width: 100%; /* 正常情况下占据整个宽度 */
    height: 35px;
    border-radius: 6px;
    opacity: 1;
    background: #f4f4f4;
    padding: 8px 12px;
    margin-bottom: 8px !important;
    display: flex;
    align-items: center;
    justify-content: space-between;
    box-sizing: border-box;
    transition: width 0.3s ease; /* 添加宽度过渡效果 */
  }

  /* 悬停时恢复固定宽度 */
  .list-item:hover {
    width: 234px; /* 悬停时恢复固定宽度 */
  }

  .item-text {
    height: 20px;
    opacity: 1;
    font-family: Source Han Sans;
    font-size: 14px;
    font-weight: normal;
    line-height: normal;
    letter-spacing: 0em;
    font-feature-settings: 'kern' on;
    color: #313131;
    flex: 1; /* 占据剩余空间 */
    margin-right: 1px; /* 与按钮组保持8px间距 */
    transition: width 0.3s ease; /* 只对宽度进行过渡，避免溢出控制闪烁 */
    white-space: nowrap; /* 不换行 */
    overflow: visible; /* 正常情况下显示全部内容 */
    text-overflow: initial; /* 正常情况下不显示省略号 */
  }

  /* 悬停时隐藏超出部分 - 立即生效，不添加过渡 */
  .list-item:hover .item-text {
    overflow: hidden; /* 悬停时隐藏溢出内容 */
    text-overflow: ellipsis; /* 悬停时显示省略号 */
    transition: none; /* 溢出控制立即生效，避免闪烁 */
  }

  .button-group {
    gap: 4px; /* 按钮之间的间距 */
    flex-shrink: 0; /* 防止按钮组被压缩 */
  }

  .confirm-btn {
    height: 20px;
    opacity: 0; /* 默认隐藏 */
    transition: opacity 0.3s ease; /* 添加过渡效果 */
    font-family: Source Han Sans;
    font-size: 14px;
    font-weight: normal;
    line-height: normal;
    letter-spacing: 0em;
    font-feature-settings: 'kern' on;
    /* 移除固定颜色，继承默认颜色 */
    padding: 0; /* 清除内边距 */
    margin-left: 0; /* 移除左边距，使用 gap 控制间距 */

    span {
      height: 20px;
      opacity: 1;
      font-family: Source Han Sans;
      font-size: 14px;
      font-weight: normal;
      line-height: normal;
      letter-spacing: 0em;
      font-feature-settings: 'kern' on;
      /* 移除固定颜色，继承默认颜色 */
    }
  }
  /* 悬浮时显示按钮 */
  .list-item:hover .confirm-btn {
    opacity: 1;
  }
  /* 选中状态字体颜色 - 使用白色，与全局样式保持一致 */
  .list-item.selected-active {
    color: white !important; /* 使用白色文本 */
  }
  .list-item.selected-active .item-text {
    color: white !important; /* 确保文本使用白色 */
  }
  .list-item.selected-active .confirm-btn,
  .list-item.selected-active .confirm-btn span {
    color: #ffffff !important; /* 按钮使用白色 */
  }
}
</style>
