<template>
  <view class="page">
    <scroll-view scroll-y class="page-scroll" @scrolltolower="loadMore">
      <common-hero title="审核列表" :use-image-bg="true"></common-hero>

      <view class="hero-panel">
        <view class="hero-title">审核列表</view>
        <view class="hero-desc">统一处理稿件、评优申报、挑战杯、青马工程、未来学术之星和团员管理审核。</view>
        <view class="hero-meta">
          <view class="metric">
            <view class="metric-value">{{ total }}</view>
            <view class="metric-label">全部记录</view>
          </view>
          <view class="metric">
            <view class="metric-value">{{ pendingCount }}</view>
            <view class="metric-label">待处理</view>
          </view>
        </view>
      </view>

      <view class="section">
        <view class="filter-card">
          <view class="filter-row">
            <view class="module-filter" @tap="openModulePicker">
              <text class="filter-text">{{ selectedModuleLabel }}</text>
              <text class="filter-arrow">▼</text>
            </view>
            <view class="status-filter" @tap="openStatusPicker">
              <text class="filter-text">{{ selectedStatusLabel }}</text>
              <text class="filter-arrow">▼</text>
            </view>
            <view class="refresh-btn" :class="{ 'refresh-btn--loading': loading }" @tap="reload">
              {{ loading ? '刷新中' : '刷新' }}
            </view>
          </view>
          <view class="search-row">
            <input
              class="search-input"
              v-model="keywords"
              confirm-type="search"
              placeholder="搜索名称/审核意见"
              placeholder-style="color:#a8b2bf;"
              @confirm="reload"
            />
            <view v-if="keywords" class="clear-btn" @tap="clearKeywords">清除</view>
            <view class="search-btn" @tap="reload">搜索</view>
          </view>
        </view>

        <view v-if="loading && !reviewList.length" class="state-card">
          <view class="state-title">正在加载审核列表...</view>
          <view class="state-desc">请稍候，正在同步后台聚合审核记录。</view>
        </view>

        <view v-else-if="loadError && !reviewList.length" class="state-card state-card--error">
          <view class="state-title">加载失败</view>
          <view class="state-desc">{{ loadError }}</view>
          <view class="state-action" @tap="reload">重新加载</view>
        </view>

        <view v-else-if="!reviewList.length" class="state-card">
          <view class="state-title">暂无审核记录</view>
          <view class="state-desc">当前筛选条件下没有待展示的数据。</view>
        </view>

        <view v-else class="review-list">
          <view v-for="item in reviewList" :key="item.id" class="review-card">
            <view class="card-head">
              <view class="card-main">
                <view class="card-title">{{ item.businessName || '未命名业务' }}</view>
                <view class="card-subtitle">{{ item.moduleName || getModuleName(item.module) }}</view>
              </view>
              <view class="status-tag" :class="`status-tag--${getStatusMeta(item.status).tone}`">
                {{ getStatusMeta(item.status).text }}
              </view>
            </view>
            <view class="info-grid">
              <view class="info-item">
                <view class="info-label">提交时间</view>
                <view class="info-value">{{ formatDateTime(item.createTime) }}</view>
              </view>
              <view class="info-item">
                <view class="info-label">当前审核人</view>
                <view class="info-value">{{ getReviewerName(item) }}</view>
              </view>
            </view>
            <view v-if="item.content" class="content-line">最新意见：{{ item.content }}</view>
            <view class="card-actions">
              <view class="action-btn" @tap="openDataModal(item)">查看数据</view>
              <view class="action-btn action-btn--primary" @tap="openFlowModal(item)">审核流</view>
            </view>
          </view>

          <view class="load-more">
            <view v-if="loadingMore" class="load-more-text">正在加载更多...</view>
            <view v-else-if="hasMore" class="load-more-text" @tap="loadMore">点击加载更多</view>
            <view v-else class="load-more-text load-more-text--end">没有更多数据了</view>
          </view>
        </view>
      </view>
    </scroll-view>

    <view v-if="showDataModal" class="modal-mask" @tap="closeDataModal">
      <view class="modal-panel" @tap.stop>
        <view class="modal-head">
          <view>
            <view class="modal-title">{{ detailTitle }}</view>
            <view class="modal-subtitle">业务ID：{{ dataCurrent && dataCurrent.pk ? dataCurrent.pk : '-' }}</view>
          </view>
          <view class="modal-close" @tap="closeDataModal">关闭</view>
        </view>
        <scroll-view scroll-y class="modal-body">
          <view v-if="detailLoading" class="state-card state-card--modal">
            <view class="state-title">正在加载详情...</view>
          </view>
          <view v-else-if="detailError" class="state-card state-card--modal state-card--error">
            <view class="state-title">详情加载失败</view>
            <view class="state-desc">{{ detailError }}</view>
          </view>
          <view v-else-if="!detailItems.length" class="state-card state-card--modal">
            <view class="state-title">暂无可展示数据</view>
          </view>
          <view v-else class="detail-list">
            <view v-for="item in detailItems" :key="item.key" class="detail-item">
              <view class="detail-label">{{ item.label }}</view>
              <view v-if="isImageField(item.key) && typeof item.value === 'string'" class="detail-image-wrap">
                <image class="detail-image" :src="item.value" mode="aspectFill" @tap="previewImage(item.value)"/>
              </view>
              <view v-else-if="isFileField(item.key)" class="file-list">
                <view
                  v-for="(file, index) in normalizeFileEntries(item.value)"
                  :key="`${item.key}-${index}`"
                  class="file-item"
                  @tap="copyText(file.url, '文件链接已复制')"
                >
                  {{ file.name }}
                </view>
              </view>
              <view v-else-if="Array.isArray(item.value)" class="array-list">
                <view v-for="(entry, index) in item.value" :key="`${item.key}-${index}`" class="array-entry">
                  <view class="array-index">第{{ index + 1 }}项</view>
                  <view v-for="(line, lineIndex) in formatArrayEntry(dataCurrent.module, entry)" :key="lineIndex"
                        class="array-line">
                    {{ line }}
                  </view>
                </view>
              </view>
              <view v-else-if="isPlainObject(item.value)" class="array-list">
                <view v-for="(line, index) in formatObjectLines(dataCurrent.module, item.value)" :key="index"
                      class="array-line">
                  {{ line }}
                </view>
              </view>
              <view v-else class="detail-value">{{
                  formatDisplayValue(dataCurrent.module, item.key, item.value)
                }}
              </view>
            </view>
          </view>
        </scroll-view>
      </view>
    </view>

    <view v-if="showFlowModal" class="modal-mask" @tap="closeFlowModal">
      <view class="modal-panel" @tap.stop>
        <view class="modal-head">
          <view>
            <view class="modal-title">{{ flowTitle }}</view>
            <view class="modal-subtitle">业务ID：{{ flowCurrent && flowCurrent.pk ? flowCurrent.pk : '-' }}</view>
          </view>
          <view class="modal-close" @tap="closeFlowModal">关闭</view>
        </view>
        <scroll-view scroll-y class="modal-body">
          <view v-if="flowLoading" class="state-card state-card--modal">
            <view class="state-title">正在加载审核流...</view>
          </view>
          <view v-else-if="flowError" class="state-card state-card--modal state-card--error">
            <view class="state-title">审核流加载失败</view>
            <view class="state-desc">{{ flowError }}</view>
          </view>
          <view v-else-if="!flowList.length" class="state-card state-card--modal">
            <view class="state-title">暂无审核流数据</view>
          </view>
          <view v-else class="flow-list">
            <view v-for="(item, index) in flowList" :key="item.id" class="flow-item">
              <view class="flow-line">
                <view class="flow-dot" :class="`flow-dot--${getStatusMeta(item.status).tone}`">{{ index + 1 }}</view>
                <view v-if="index < flowList.length - 1" class="flow-rail"></view>
              </view>
              <view class="flow-card">
                <view class="flow-head">
                  <view class="flow-user">{{ getReviewerName(item) }}</view>
                  <view class="status-tag" :class="`status-tag--${getStatusMeta(item.status).tone}`">
                    {{ getStatusMeta(item.status).text }}
                  </view>
                </view>
                <view class="flow-time">创建时间：{{ formatDateTime(item.createTime) }}</view>
                <view v-if="item.content" class="flow-content">审核意见：{{ item.content }}</view>
                <view v-if="canReview(item)" class="flow-actions">
                  <view class="review-btn review-btn--approve" @tap="openActionModal('approve', item)">通过</view>
                  <view class="review-btn review-btn--reject" @tap="openActionModal('reject', item)">拒绝</view>
                </view>
              </view>
            </view>
          </view>
        </scroll-view>
      </view>
    </view>

    <view v-if="actionVisible" class="action-mask" @tap="closeActionModal">
      <view class="action-panel" @tap.stop>
        <view class="action-title">{{ actionType === 'approve' ? '审核通过' : '审核拒绝' }}</view>
        <textarea
          class="action-textarea"
          :value="actionContent"
          placeholder="请输入审核意见（非必填）"
          placeholder-style="color:#a8b2bf;"
          @input="handleActionInput"
        />
        <view class="action-footer">
          <view class="ghost-btn" @tap="closeActionModal">取消</view>
          <view class="primary-btn" :class="{ 'primary-btn--loading': submitting }" @tap="submitReview">
            {{ submitting ? '提交中' : '确认' }}
          </view>
        </view>
      </view>
    </view>

    <uv-picker
      ref="modulePicker"
      title="选择模块"
      :columns="modulePickerColumns"
      :defaultIndex="[moduleIndex]"
      keyName="text"
      @close="noop"
      @cancel="noop"
      @confirm="confirmModulePicker"
    ></uv-picker>

    <uv-picker
      ref="statusPicker"
      title="选择状态"
      :columns="statusPickerColumns"
      :defaultIndex="[statusIndex]"
      keyName="text"
      @close="noop"
      @cancel="noop"
      @confirm="confirmStatusPicker"
    ></uv-picker>
  </view>
</template>

<script>
import CommonHero from '../../components/common-hero/common-hero.vue'
import {getCurrentUser} from '../assistant/chat-service'
import {ensureLoggedIn} from '../../utils/request'
import {getUserProfile} from '../mine/service'
import {
  FILE_FIELDS,
  IMAGE_FIELDS,
  MODULE_OPTIONS,
  buildDescriptionItems,
  formatArrayEntry,
  formatDateTime,
  formatDisplayValue,
  formatObjectLines,
  getModuleDetail,
  getModuleName,
  getReviewStatusMeta,
  hasReviewListAuthority,
  isPlainObject,
  listReviewList,
  normalizeFileEntries,
  pageGroupReviewList,
  updateReviewList
} from '../../utils/gxmu/review-list-service'

export default {
  components: {
    CommonHero
  },
  data() {
    return {
      loading: false,
      loadError: '',
      reviewList: [],
      total: 0,
      currentPage: 1,
      pageSize: 10,
      loadingMore: false,
      hasMore: true,
      keywords: '',
      selectedModule: '',
      selectedStatus: '',
      dataCurrent: null,
      showDataModal: false,
      detailLoading: false,
      detailError: '',
      detailItems: [],
      flowCurrent: null,
      showFlowModal: false,
      flowLoading: false,
      flowError: '',
      flowList: [],
      actionVisible: false,
      actionType: 'approve',
      actionContent: '',
      actionTarget: null,
      submitting: false,
      loginUserId: 0,
      loginUsername: '',
      loginRealName: ''
    }
  },
  computed: {
    modulePickerColumns() {
      return [[...MODULE_OPTIONS.map((item) => ({text: item.label, value: item.value}))]]
    },
    statusOptions() {
      return [
        {text: '全部状态', value: ''},
        {text: '待审核', value: 0},
        {text: '通过', value: 1},
        {text: '拒绝', value: 2}
      ]
    },
    statusPickerColumns() {
      return [[...this.statusOptions]]
    },
    moduleIndex() {
      const index = MODULE_OPTIONS.findIndex((item) => item.value === this.selectedModule)
      return index > -1 ? index : 0
    },
    statusIndex() {
      const index = this.statusOptions.findIndex((item) => item.value === this.selectedStatus)
      return index > -1 ? index : 0
    },
    selectedModuleLabel() {
      const item = MODULE_OPTIONS.find((option) => option.value === this.selectedModule)
      return (item && item.label) || '全部模块'
    },
    selectedStatusLabel() {
      const item = this.statusOptions.find((option) => option.value === this.selectedStatus)
      return (item && item.text) || '全部状态'
    },
    pendingCount() {
      return this.reviewList.filter((item) => Number(item.status) === 0).length
    },
    detailTitle() {
      const current = this.dataCurrent || {}
      return `${current.moduleName || getModuleName(current.module)}详情`
    },
    flowTitle() {
      const current = this.flowCurrent || {}
      return `${current.moduleName || getModuleName(current.module)}审核流`
    }
  },
  onLoad() {
    if (!ensureLoggedIn({redirectUrl: '/pages/gxmu/review-list'})) {
      return
    }
    const user = getCurrentUser()
    this.loginUserId = Number(user.userId || 0)
    this.verifyPermissionAndLoad()
  },
  onPullDownRefresh() {
    this.loadData(true)
  },
  methods: {
    formatDateTime,
    formatDisplayValue,
    formatArrayEntry,
    formatObjectLines,
    isPlainObject,
    normalizeFileEntries,
    getModuleName,
    getStatusMeta(status) {
      return getReviewStatusMeta(status)
    },
    isImageField(key) {
      return IMAGE_FIELDS.has(key)
    },
    isFileField(key) {
      return FILE_FIELDS.has(key)
    },
    showToast(title) {
      uni.showToast({
        title,
        icon: 'none'
      })
    },
    async verifyPermissionAndLoad() {
      try {
        const profile = await getUserProfile()
        if (!hasReviewListAuthority(profile)) {
          this.showToast('暂无审核列表权限')
          setTimeout(() => {
            uni.navigateBack()
          }, 500)
          return
        }
        this.applyLoginUser(profile)
      } catch (error) {
        this.showToast((error && error.message) || '权限校验失败')
        setTimeout(() => {
          uni.navigateBack()
        }, 500)
        return
      }
      this.loadData(true)
    },
    applyLoginUser(profile) {
      const user = profile || {}
      const nestedUser = user.user || {}
      const userId = Number(user.userId || user.id || nestedUser.userId || nestedUser.id || this.loginUserId || 0)
      this.loginUserId = userId || 0
      this.loginUsername = String(user.username || nestedUser.username || '').trim()
      this.loginRealName = String(user.realName || user.name || nestedUser.realName || nestedUser.name || '').trim()
    },
    getReviewerId(item) {
      const user = (item && item.user) || {}
      return Number((item && item.userId) || user.userId || user.id || 0)
    },
    getReviewerIdentityList(item) {
      const user = (item && item.user) || {}
      return [
        user.username,
        user.realName,
        user.name
      ].map((value) => String(value || '').trim()).filter(Boolean)
    },
    getReviewerName(item) {
      const user = (item && item.user) || {}
      return user.realName || user.username || '未分配审核人'
    },
    openModulePicker() {
      this.$nextTick(() => {
        this.$refs.modulePicker && this.$refs.modulePicker.open()
      })
    },
    confirmModulePicker(event) {
      const value = (((event || {}).value || [])[0] || {}).value || ''
      this.selectedModule = value
      this.loadData(true)
    },
    openStatusPicker() {
      this.$nextTick(() => {
        this.$refs.statusPicker && this.$refs.statusPicker.open()
      })
    },
    confirmStatusPicker(event) {
      const option = (((event || {}).value || [])[0] || {})
      this.selectedStatus = option.value === undefined ? '' : option.value
      this.loadData(true)
    },
    noop() {
      return
    },
    clearKeywords() {
      this.keywords = ''
      this.reload()
    },
    reload() {
      this.loadData(true)
    },
    async loadData(reset = false) {
      if (this.loading || this.loadingMore) {
        if (reset) {
          uni.stopPullDownRefresh()
        }
        return
      }
      const nextPage = reset ? 1 : this.currentPage
      if (!reset && !this.hasMore) {
        return
      }
      if (reset) {
        this.currentPage = 1
        this.hasMore = true
        this.reviewList = []
        this.total = 0
        this.loading = true
        this.loadError = ''
      } else {
        this.loadingMore = true
      }
      try {
        const params = {
          page: nextPage,
          limit: this.pageSize,
          backendAccess: true
        }
        if (this.selectedModule) params.module = this.selectedModule
        if (this.selectedStatus !== '') params.status = this.selectedStatus
        if (this.keywords) params.keywords = this.keywords
        const result = await pageGroupReviewList(params)
        const list = (result && (result.list || result.rows || result.records)) || []
        const nextList = Array.isArray(list) ? list : []
        const totalValue = result && (result.count !== undefined ? result.count : result.total)
        const hasTotal = totalValue !== undefined && totalValue !== null && totalValue !== ''
        const nextReviewList = reset ? nextList : this.reviewList.concat(nextList)
        this.reviewList = nextReviewList
        this.total = hasTotal ? Number(totalValue || 0) : nextReviewList.length
        this.currentPage = nextPage + 1
        this.hasMore = hasTotal ? nextReviewList.length < this.total : nextList.length >= this.pageSize
      } catch (error) {
        this.loadError = (error && error.message) || '审核列表加载失败'
        this.showToast(this.loadError)
      } finally {
        this.loading = false
        this.loadingMore = false
        uni.stopPullDownRefresh()
      }
    },
    loadMore() {
      if (!this.loading && !this.loadingMore && this.hasMore) {
        this.loadData(false)
      }
    },
    async openDataModal(item) {
      this.dataCurrent = item
      this.showDataModal = true
      this.detailLoading = true
      this.detailError = ''
      this.detailItems = []
      try {
        const detail = await getModuleDetail(item.module, item.pk)
        this.detailItems = buildDescriptionItems(item.module, detail || {})
      } catch (error) {
        this.detailError = (error && error.message) || '数据加载失败'
      } finally {
        this.detailLoading = false
      }
    },
    closeDataModal() {
      this.showDataModal = false
      this.dataCurrent = null
      this.detailItems = []
      this.detailError = ''
      this.detailLoading = false
    },
    async openFlowModal(item) {
      this.flowCurrent = item
      this.showFlowModal = true
      await this.loadFlowData()
    },
    closeFlowModal() {
      this.showFlowModal = false
      this.flowCurrent = null
      this.flowList = []
      this.flowError = ''
      this.flowLoading = false
      this.closeActionModal()
    },
    async loadFlowData() {
      const current = this.flowCurrent || {}
      if (!current.module || !current.pk) {
        this.flowList = []
        return
      }
      this.flowLoading = true
      this.flowError = ''
      try {
        const result = await listReviewList({
          module: current.module,
          pk: current.pk
        })
        const list = Array.isArray(result) ? result : []
        this.flowList = [...list].sort((left, right) => Number(right.id || 0) - Number(left.id || 0))
      } catch (error) {
        this.flowList = []
        this.flowError = (error && error.message) || '审核流加载失败'
      } finally {
        this.flowLoading = false
      }
    },
    canReview(item) {
      if (Number(item && item.status) !== 0) {
        return false
      }
      const reviewerId = this.getReviewerId(item)
      const loginUserId = Number(this.loginUserId || 0)
      if (reviewerId && loginUserId) {
        return reviewerId === loginUserId
      }
      const loginIdentityList = [this.loginUsername, this.loginRealName]
        .map((value) => String(value || '').trim())
        .filter(Boolean)
      if (!loginIdentityList.length) {
        return false
      }
      return this.getReviewerIdentityList(item).some((value) => loginIdentityList.includes(value))
    },
    openActionModal(type, item) {
      if (!this.canReview(item)) {
        this.showToast('当前记录不是由你审核')
        return
      }
      this.actionType = type
      this.actionTarget = item
      this.actionContent = ''
      this.actionVisible = true
    },
    closeActionModal() {
      this.actionVisible = false
      this.actionTarget = null
      this.actionContent = ''
    },
    handleActionInput(event) {
      this.actionContent = (event.detail && event.detail.value) || ''
    },
    async submitReview() {
      if (this.submitting) {
        return
      }
      const target = this.actionTarget
      if (!target || !target.id) {
        this.closeActionModal()
        return
      }
      if (!this.canReview(target)) {
        this.closeActionModal()
        this.showToast('当前记录不是由你审核')
        return
      }
      this.submitting = true
      try {
        await updateReviewList({
          id: target.id,
          status: this.actionType === 'approve' ? 1 : 2,
          module: target.module,
          pk: target.pk,
          sortNumber: target.sortNumber,
          content: String(this.actionContent || '').trim() || undefined
        })
        this.showToast('审核已提交')
        this.closeActionModal()
        await this.loadFlowData()
        await this.loadData(true)
      } catch (error) {
        this.showToast((error && error.message) || '审核提交失败')
      } finally {
        this.submitting = false
      }
    },
    previewImage(url) {
      if (!url) {
        return
      }
      uni.previewImage({
        urls: [url],
        current: url
      })
    },
    copyText(text, title = '已复制') {
      if (!text) {
        this.showToast('暂无可复制内容')
        return
      }
      uni.setClipboardData({
        data: String(text),
        success: () => this.showToast(title)
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.page {
  min-height: 100vh;
  background: linear-gradient(180deg, #f7fbff 0%, #edf4f9 38%, #f6f7f9 100%);
}

.page-scroll {
  height: 100vh;
  padding-bottom: 42rpx;
}

.hero-panel,
.section {
  margin: 24rpx;
}

.hero-panel {
  padding: 30rpx;
  border-radius: 30rpx;
  background: linear-gradient(135deg, #0d6fba 0%, #1496f2 62%, #5bc2ff 100%);
  box-shadow: 0 18rpx 42rpx rgba(20, 118, 194, 0.16);
  color: #fff;
}

.hero-title {
  font-size: 38rpx;
  font-weight: 800;
}

.hero-desc {
  margin-top: 12rpx;
  font-size: 24rpx;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.86);
}

.hero-meta {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16rpx;
  margin-top: 28rpx;
}

.metric {
  padding: 20rpx 14rpx;
  border-radius: 22rpx;
  background: rgba(255, 255, 255, 0.13);
  text-align: center;
}

.metric-value {
  font-size: 34rpx;
  font-weight: 800;
}

.metric-label {
  margin-top: 8rpx;
  font-size: 21rpx;
  color: rgba(255, 255, 255, 0.78);
}

.filter-card,
.review-card,
.state-card,
.flow-card,
.detail-item {
  border: 1rpx solid rgba(20, 150, 242, 0.08);
  background: rgba(255, 255, 255, 0.94);
  box-shadow: 0 10rpx 28rpx rgba(34, 94, 142, 0.08);
}

.filter-card {
  padding: 22rpx;
  border-radius: 28rpx;
}

.filter-row,
.search-row,
.card-head,
.card-actions,
.modal-head,
.action-footer,
.flow-head,
.flow-actions {
  display: flex;
  align-items: center;
}

.filter-row {
  gap: 16rpx;
}

  .module-filter,
  .status-filter,
  .search-input {
    flex: 1;
    min-width: 0;
  height: 78rpx;
  border-radius: 22rpx;
  background: #f4f8fb;
}

  .module-filter,
  .status-filter {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 22rpx;
  }

  .status-filter {
    flex: 0 0 190rpx;
  }

.filter-text {
  font-size: 24rpx;
  font-weight: 600;
  color: #233142;
}

.filter-arrow {
  font-size: 20rpx;
  color: #7890a8;
}

.refresh-btn,
.search-btn,
.clear-btn {
  height: 78rpx;
  padding: 0 24rpx;
  border-radius: 22rpx;
  font-size: 24rpx;
  font-weight: 700;
  line-height: 78rpx;
  white-space: nowrap;
}

.refresh-btn,
.search-btn {
  background: #1496f2;
  color: #fff;
}

.refresh-btn--loading {
  opacity: 0.72;
}

.search-row {
  gap: 14rpx;
  margin-top: 16rpx;
}

.search-input {
  box-sizing: border-box;
  padding: 0 22rpx;
  font-size: 24rpx;
  color: #263342;
}

.clear-btn {
  background: #eef3f7;
  color: #69798b;
}

.state-card {
  margin-top: 22rpx;
  padding: 34rpx 28rpx;
  border-radius: 28rpx;
  text-align: center;
}

.state-card--error {
  border-color: rgba(226, 80, 80, 0.16);
}

.state-card--modal {
  margin-top: 0;
  box-shadow: none;
}

.state-title {
  font-size: 28rpx;
  font-weight: 800;
  color: #263342;
}

.state-desc {
  margin-top: 10rpx;
  font-size: 23rpx;
  line-height: 1.6;
  color: #7a8797;
}

.state-action {
  display: inline-block;
  margin-top: 18rpx;
  padding: 14rpx 24rpx;
  border-radius: 999rpx;
  background: #e8f4ff;
  color: #1496f2;
  font-size: 23rpx;
  font-weight: 700;
}

.review-list {
  margin-top: 22rpx;
}

.review-card {
  padding: 26rpx;
  border-radius: 28rpx;
}

.review-card + .review-card {
  margin-top: 18rpx;
}

.card-head {
  justify-content: space-between;
  gap: 18rpx;
}

.card-main {
  flex: 1;
  min-width: 0;
}

.card-title {
  font-size: 29rpx;
  font-weight: 800;
  color: #222b36;
  line-height: 1.35;
}

.card-subtitle {
  margin-top: 8rpx;
  font-size: 23rpx;
  color: #718092;
}

.status-tag {
  padding: 10rpx 18rpx;
  border-radius: 999rpx;
  font-size: 22rpx;
  font-weight: 700;
  white-space: nowrap;
}

.status-tag--success {
  background: #e8f8ef;
  color: #1f9d5b;
}

.status-tag--danger {
  background: #fff0ee;
  color: #d94a3a;
}

.status-tag--warning {
  background: #fff6df;
  color: #b97900;
}

.info-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16rpx;
  margin-top: 22rpx;
}

.info-item {
  padding: 18rpx;
  border-radius: 20rpx;
  background: #f6f9fc;
}

.info-label {
  font-size: 21rpx;
  color: #8a97a6;
}

.info-value {
  margin-top: 8rpx;
  font-size: 23rpx;
  font-weight: 700;
  color: #263342;
  word-break: break-all;
}

.content-line {
  margin-top: 18rpx;
  padding: 18rpx;
  border-radius: 20rpx;
  background: #fffaf0;
  font-size: 23rpx;
  line-height: 1.6;
  color: #7a5b21;
}

.card-actions {
  gap: 16rpx;
  margin-top: 22rpx;
}

.action-btn {
  flex: 1;
  height: 76rpx;
  border-radius: 22rpx;
  background: #edf4fa;
  color: #2e5c7e;
  font-size: 24rpx;
  font-weight: 800;
  line-height: 76rpx;
  text-align: center;
}

.action-btn--primary {
  background: #1496f2;
  color: #fff;
}

.load-more {
  padding: 28rpx 0 8rpx;
  text-align: center;
}

.load-more-text {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 220rpx;
  height: 64rpx;
  padding: 0 26rpx;
  border-radius: 999rpx;
  background: #e8f4ff;
  color: #1496f2;
  font-size: 23rpx;
  font-weight: 800;
}

.load-more-text--end {
  background: transparent;
  color: #8a97a6;
  font-weight: 600;
}

.modal-mask,
.action-mask {
  position: fixed;
  z-index: 20;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  align-items: flex-end;
  background: rgba(12, 24, 38, 0.46);
}

.modal-panel {
  width: 100%;
  max-height: 86vh;
  border-radius: 34rpx 34rpx 0 0;
  background: #f6f8fa;
  overflow: hidden;
}

.modal-head {
  justify-content: space-between;
  padding: 26rpx 28rpx;
  background: #fff;
  border-bottom: 1rpx solid #edf1f5;
}

.modal-title {
  font-size: 31rpx;
  font-weight: 800;
  color: #222b36;
}

.modal-subtitle {
  margin-top: 8rpx;
  font-size: 22rpx;
  color: #8290a0;
}

.modal-close {
  padding: 12rpx 20rpx;
  border-radius: 999rpx;
  background: #eef4f9;
  color: #55697d;
  font-size: 23rpx;
  font-weight: 700;
}

.modal-body {
  box-sizing: border-box;
  max-height: calc(86vh - 112rpx);
  padding: 22rpx;
}

.detail-item {
  padding: 24rpx;
  border-radius: 24rpx;
}

.detail-item + .detail-item {
  margin-top: 16rpx;
}

.detail-label {
  font-size: 22rpx;
  font-weight: 800;
  color: #7b8998;
}

.detail-value,
.array-line {
  margin-top: 10rpx;
  font-size: 25rpx;
  line-height: 1.7;
  color: #273443;
  white-space: pre-wrap;
  word-break: break-all;
}

.detail-image {
  margin-top: 14rpx;
  width: 180rpx;
  height: 180rpx;
  border-radius: 18rpx;
  background: #eef3f7;
}

.file-item {
  margin-top: 12rpx;
  padding: 16rpx 18rpx;
  border-radius: 18rpx;
  background: #edf7ff;
  color: #147cc6;
  font-size: 24rpx;
  word-break: break-all;
}

.array-entry {
  margin-top: 14rpx;
  padding: 18rpx;
  border-radius: 18rpx;
  background: #f5f8fb;
}

.array-entry + .array-entry {
  margin-top: 14rpx;
}

.array-index {
  font-size: 22rpx;
  font-weight: 800;
  color: #41556a;
}

.flow-list {
  padding-bottom: 10rpx;
}

.flow-item {
  display: flex;
  align-items: stretch;
}

.flow-line {
  position: relative;
  width: 66rpx;
  display: flex;
  justify-content: center;
}

.flow-dot {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44rpx;
  height: 44rpx;
  border-radius: 50%;
  font-size: 20rpx;
  font-weight: 800;
}

.flow-dot--success {
  background: #dcf6e8;
  color: #1f9d5b;
}

.flow-dot--danger {
  background: #ffe5e1;
  color: #d94a3a;
}

.flow-dot--warning {
  background: #fff1cc;
  color: #b97900;
}

.flow-rail {
  position: absolute;
  top: 44rpx;
  bottom: 0;
  width: 2rpx;
  background: #d9e4ee;
}

.flow-card {
  flex: 1;
  min-width: 0;
  margin-bottom: 18rpx;
  padding: 22rpx;
  border-radius: 24rpx;
}

.flow-head {
  justify-content: space-between;
  gap: 14rpx;
}

.flow-user {
  font-size: 27rpx;
  font-weight: 800;
  color: #233142;
}

.flow-time,
.flow-content {
  margin-top: 12rpx;
  font-size: 23rpx;
  line-height: 1.6;
  color: #748397;
}

.flow-actions {
  gap: 14rpx;
  margin-top: 18rpx;
}

.review-btn {
  flex: 1;
  height: 68rpx;
  border-radius: 20rpx;
  font-size: 23rpx;
  font-weight: 800;
  line-height: 68rpx;
  text-align: center;
}

.review-btn--approve {
  background: #1496f2;
  color: #fff;
}

.review-btn--reject {
  background: #fff0ee;
  color: #d94a3a;
}

.action-mask {
  z-index: 30;
  align-items: center;
  justify-content: center;
  padding: 34rpx;
  box-sizing: border-box;
}

.action-panel {
  width: 100%;
  padding: 28rpx;
  border-radius: 28rpx;
  background: #fff;
}

.action-title {
  font-size: 31rpx;
  font-weight: 800;
  color: #233142;
}

.action-textarea {
  box-sizing: border-box;
  width: 100%;
  min-height: 210rpx;
  margin-top: 22rpx;
  padding: 20rpx;
  border-radius: 22rpx;
  background: #f5f8fb;
  font-size: 24rpx;
  line-height: 1.6;
  color: #263342;
}

.action-footer {
  gap: 16rpx;
  margin-top: 22rpx;
}

.ghost-btn,
.primary-btn {
  flex: 1;
  height: 76rpx;
  border-radius: 22rpx;
  font-size: 24rpx;
  font-weight: 800;
  line-height: 76rpx;
  text-align: center;
}

.ghost-btn {
  background: #eef4f9;
  color: #566b80;
}

.primary-btn {
  background: #1496f2;
  color: #fff;
}

.primary-btn--loading {
  opacity: 0.72;
}
</style>
