StuTrainingDetailDialog.vue 5.46 KB
<template>
  <Dialog :title="dialogTitle" v-model="dialogVisible" width="700px">
    <div v-loading="loading">
      <!-- 基础信息 -->
      <el-descriptions v-if="detailData" border :column="1" label-width="120px">
        <el-descriptions-item label="学员">
          {{ detailData.username || '-' }}
        </el-descriptions-item>
        <el-descriptions-item label="手机号">
          {{ detailData.phone || '-' }}
        </el-descriptions-item>
      </el-descriptions>

      <!-- 训练单元列表 -->
      <div v-if="detailData.units && detailData.units.length > 0" class="unit-section">
        <h3 class="section-title">训练单元列表</h3>

        <div v-for="(unit, unitIndex) in detailData.units" :key="unit.id || unitIndex" class="unit-card">
          <el-descriptions border :column="1" label-width="120px" size="small">
            <el-descriptions-item label="单元名称">
              {{ unit.name || '-' }}
            </el-descriptions-item>
            <el-descriptions-item label="单元类型">
              {{ formatUnitType(unit.unitType) }}
            </el-descriptions-item>
            <el-descriptions-item label="单元顺序">
              {{ unit.sortOrder || '-' }}
            </el-descriptions-item>
          </el-descriptions>

          <!-- 动作列表 -->
          <div v-if="unit.exercises && unit.exercises.length > 0" class="exercise-section">
            <h4 class="sub-title">动作列表</h4>

            <div v-for="(exercise, exIndex) in unit.exercises" :key="exercise.exerciseId || exIndex" class="exercise-card">
              <el-descriptions border :column="1" label-width="120px" size="small">
                <el-descriptions-item label="排序">
                  {{ exercise.innerOrder || '-' }}
                </el-descriptions-item>
                <el-descriptions-item label="动作名称">
                  {{ exercise.name || '-' }}
                </el-descriptions-item>
                <el-descriptions-item label="训练组详情">
                  <el-table
                    v-if="exercise.sets && exercise.sets.length > 0"
                    :data="exercise.sets"
                    border
                    size="small"
                    stripe
                  >
                    <el-table-column label="组号" width="80">
                      <template #default="scope">
                        {{ scope.$index + 1 }}
                      </template>
                    </el-table-column>
                    <el-table-column prop="weight" label="重量(kg)" width="100" />
                    <el-table-column prop="reps" label="次数" width="80" />
                    <el-table-column label="时长(分钟)" width="100">
                      <template #default="scope">
                        {{ scope.row.duration ? (scope.row.duration / 60).toFixed(1) : 0 }}
                      </template>
                    </el-table-column>
                    <el-table-column prop="restTime" label="休息(秒)" width="100" />
                  </el-table>
                  <div v-else class="empty-text">无训练组数据</div>
                </el-descriptions-item>
              </el-descriptions>
            </div>
          </div>
        </div>
      </div>
    </div>

    <template #footer>
      <el-button @click="dialogVisible = false">关 闭</el-button>
    </template>
  </Dialog>
</template>

<script setup>
import { ref, watch, computed } from 'vue'
import { stuTrainingRecordsApi } from '@/api/store/training/stuTrainingRecord'

// ===================== Props & Emits =====================
const props = defineProps({
  visible: { type: Boolean, default: false },
  id: { type: [Number, String], default: null }
})
const emit = defineEmits(['update:visible'])

const dialogVisible = computed({
  get: () => props.visible,
  set: (val) => emit('update:visible', val)
})
const dialogTitle = ref('训练记录详情')

// ===================== 状态 =====================
const loading = ref(false)
const detailData = ref({})

// ===================== 工具函数 =====================
const formatUnitType = (type) => {
  if (type === 1) return '动作组'
  if (type === 2) return '超级组'
  return '未知类型'
}

// ===================== 数据加载 =====================
const getStuTrainingDetail = async (id) => {
  loading.value = true
  try {
    const res = await stuTrainingRecordsApi.getStuTrainingRecordDetail(id)
    detailData.value = res || {}
  } finally {
    loading.value = false
  }
}

// ===================== 监听弹窗打开 / id 变化加载详情 =====================
watch(
  [() => props.visible, () => props.id],
  ([visible, newId]) => {
    if (visible && newId) getStuTrainingDetail(newId)
  },
  { immediate: true }
)
</script>

<style scoped>
.unit-section {
  margin-top: 20px;
}

.section-title {
  margin: 0 0 12px;
  padding-left: 10px;
  font-size: 16px;
  font-weight: 600;
  color: var(--el-text-color-primary);
  border-left: 4px solid var(--el-color-primary);
}

.sub-title {
  margin: 16px 0 10px;
  font-size: 14px;
  font-weight: 600;
  color: var(--el-color-primary);
}

.unit-card {
  padding: 15px;
  margin-bottom: 20px;
  background: var(--el-fill-color-lighter);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 8px;
}

.exercise-card {
  padding: 10px;
  margin-top: 15px;
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-radius: 6px;
}

.empty-text {
  padding: 10px;
  color: var(--el-text-color-secondary);
}
</style>