templatesForm.vue 7.58 KB
<template>
  <div class="page-container">
    <!-- 顶部搜索与操作栏 -->
    <ContentWrap class="plan-search-bar">
      <div class="search-item">
        <div class="search-left">
          <label class="search-label">模板名称: </label>
          <el-input class="search-input" placeholder="请输入" v-model="searchForm.name" @keyup.enter="handleSearch" />
        </div>
        <div class="search-buttons">
          <el-button type="primary" @click="handleSearch" :loading="loading">
            <el-icon>
              <Search />
            </el-icon>
            搜索
          </el-button>
          <el-button @click="handleReset">
            <el-icon>
              <Refresh />
            </el-icon>
            重置
          </el-button>
          <el-button type="primary" plain @click="handleAdd">
            <el-icon>
              <Plus />
            </el-icon>
            新增
          </el-button>
        </div>
      </div>
    </ContentWrap>

    <ContentWrap>
      <!-- 数据列表 -->
      <div class="table-container">
        <!-- 加载状态 -->
        <el-skeleton v-if="loading" :rows="5" animated />
        <el-table v-else :data="tableData" style="width: 100%" empty-text="暂无模板数据">
          <el-table-column prop="name" label="模板名称" align="center" min-width="200" />
          <el-table-column prop="cover" label="封面" align="center" min-width="120">
            <template #default="scope">
              <div class="cover-wrapper">
                <!-- 优先显示行数据封面,无则显示占位图 -->
                <img class="cover-img" :src="scope.row.urlCover || lostImg" alt="模板封面" align="center" />
              </div>
            </template>
          </el-table-column>
          <el-table-column prop="createTime" label="创建时间" align="center" min-width="200" :formatter="dateFormatter" />
          <el-table-column prop="updateTime" label="更新时间" align="center" min-width="200" :formatter="dateFormatter" />
          <el-table-column label="操作" align="center" min-width="180">
            <template #default="scope">
              <el-button type="text" @click="handleEdit(scope.row)" class="opt-edit">
                编辑
              </el-button>
              <el-button type="text" @click="handleDelete(scope.row)" class="opt-delete">
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>

        <!-- 分页栏:修复类型绑定 + 拼写错误 totle → total -->
        <div class="pagination-wrapper" v-if="!loading">
          <el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
            :total="pagination.total" :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
            @size-change="handleSizeChange" @current-change="handleCurrentChange" />
        </div>
      </div>
    </ContentWrap>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import lostImg from '@/assets/imgs/lost.png'
import { Search, Refresh, Plus } from '@element-plus/icons-vue'
import { TrainingTemplatesApi } from '@/api/store/training/templates'
import { dateFormatter } from '@/utils/formatTime'
import { watch } from 'vue'

// 路由实例
const router = useRouter()
const route = useRoute()
// 加载状态(模板中已使用)
const loading = ref(false)
// 表格数据
const tableData = ref<any[]>([])

// 分页参数:统一用 number 类型(适配 el-pagination)
const pagination = reactive({
  currentPage: 1,    // 当前页码(替换原 pageNo,统一用 Element Plus 标准命名)
  pageSize: 10,      // 每页条数(number 类型,适配组件)
  total: 0,          // 总条数
  userId: '',        // 创建人
  createTime: '',    // 创建时间
  groupId: ''        // 大类id
})

// 搜索表单(统一管理筛选条件)
const searchForm = reactive({
  name: '',          // 模板名称
})

// 监听路由,只要模板新增保存完毕回到这个页面,就刷新列表显示新增的数据
watch(
  () => route.fullPath,
  () => {
    fetchList()
  }
)

// 获取列表数据(核心修复:参数类型 + 接口传参)
const fetchList = async () => {
  loading.value = true
  try {
    // 组装接口参数:转成字符串(如果接口要求 string 类型)
    const params = {
      pageNo: pagination.currentPage + '',
      pageSize: pagination.pageSize + '',
      name: searchForm.name,
      userId: pagination.userId,
      createTime: pagination.createTime,
      groupId: pagination.groupId
    }
    const res = await TrainingTemplatesApi.getTrainingTemplatesPage(params)
    console.log('返回模板分页数据', res);
    tableData.value = res.list || []
    pagination.total = res.total || 0
  } catch (err) {
    ElMessage.error('获取数据失败')
    tableData.value = []
    pagination.total = 0
  } finally {
    loading.value = false
  }
}

// 搜索
const handleSearch = () => {
  pagination.currentPage = 1  // 搜索后重置页码
  fetchList()
}

// 重置
const handleReset = () => {
  searchForm.name = ''
  pagination.currentPage = 1
  fetchList()  // 重置后刷新列表
}

// 新增
const handleAdd = () => {
  router.push('/store/training/templates/add')
}
// 编辑
const handleEdit = (row: any) => {
  router.push({
    path: '/store/training/templates/add',
    query: { id: row.id }
  })
}
// 删除
const handleDelete = (row: any) => {
  ElMessageBox.confirm(
    `确定要删除模板「${row.name}」吗?`,
    '提示',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).then(async () => {
    try {
      await TrainingTemplatesApi.deleteTrainingTemplates(row.id)
      ElMessage.success('删除成功!')
      fetchList()  // 删除后刷新列表
    } catch (err) {
      ElMessage.error('删除失败')
    }
  }).catch(() => {
    ElMessage.info('已取消删除')
  })
}

// 分页-切换每页条数
const handleSizeChange = (val: number) => {
  pagination.pageSize = val
  pagination.currentPage = 1  // 切换条数后重置页码
  fetchList()  // 刷新列表
}

// 分页-切换页码
const handleCurrentChange = (val: number) => {
  pagination.currentPage = val
  fetchList()  // 刷新列表
}
const load = async () => {
  const res = await TrainingTemplatesApi.getTrainingTemplates(39)
  console.log('编辑返回数据', res)
}

// 初始化加载列表
onMounted(async () => {
  fetchList()
  // load()
})
</script>

<style setup>
.page-container {
  padding: 20px;
}

.plan-search-bar {
  margin-bottom: 20px;
  box-shadow: 0 1px 2px rgb(0 0 0 / 5%);
}

/* 2. 搜索项flex布局(核心,让标签、输入框、按钮横向对齐) */
.search-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 15px 20px;
}

.search-bar {
  display: flex;
  margin-right: 20px;

  /* justify-content: space-between; */
  align-items: center;
  margin-bottom: 20px;
}

.search-label {
  white-space: nowrap;
}

.search-input {
  width: 300px;
  margin-right: 10px;
}

.search-buttons {
  display: flex;
  gap: 10px;
  margin-left: 10px;
}

.table-container {
  padding: 20px;
  background: #fff;
  border-radius: 4px;
}

.pagination-wrapper {
  display: flex;
  padding: 12px 16px;
  justify-content: flex-end;
}

.cover-wrapper {
  width: 80px;
  height: 60px;
  overflow: hidden;
  border-radius: 4px;
}

.cover-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.opt-edit {
  color: #409eff;
}

.opt-delete {
  color: #f56c6c;
}
</style>