1840 lines
66 KiB
Vue
1840 lines
66 KiB
Vue
<template>
|
||
<div class="p-2 page-container">
|
||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||
<div v-show="showSearch" class="mb-[10px]">
|
||
<el-card shadow="hover">
|
||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||
<el-row :gutter="20">
|
||
<el-col :span="6">
|
||
<el-form-item label="活动名称" prop="activityName">
|
||
<el-input v-model="queryParams.activityName" placeholder="请输入活动名称" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="活动类型" prop="activityType">
|
||
<el-select v-model="queryParams.activityType" placeholder="请选择活动类型" clearable style="width: 100%">
|
||
<el-option v-for="dict in stgl_activity_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="活动状态" prop="activityStatus">
|
||
<el-select v-model="queryParams.activityStatus" placeholder="请选择活动状态" clearable style="width: 100%">
|
||
<el-option v-for="dict in stgl_activity_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="报名状态" prop="registrationStatus">
|
||
<el-select v-model="queryParams.registrationStatus" placeholder="请选择报名状态" clearable style="width: 100%">
|
||
<el-option v-for="dict in stgl_registration_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="6">
|
||
<el-form-item label="主办方" prop="organizerName">
|
||
<el-input v-model="queryParams.organizerName" placeholder="请输入主办方名称" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="是否已办" prop="isFinished">
|
||
<el-select v-model="queryParams.isFinished" placeholder="请选择是否已办" clearable style="width: 100%">
|
||
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="关联社团/队伍" prop="relatedClubId">
|
||
<ClubSelector v-model="queryParams.relatedClub" @change="handleQueryRelatedClubChange" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="活动时间">
|
||
<el-date-picker
|
||
v-model="dateRange"
|
||
type="datetimerange"
|
||
range-separator="至"
|
||
start-placeholder="开始日期"
|
||
end-placeholder="结束日期"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row>
|
||
<el-col :span="24" class="search-btn-container">
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</el-card>
|
||
</div>
|
||
</transition>
|
||
|
||
<el-card shadow="never" class="button-card table-card">
|
||
<template #header>
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activity:add']">新增</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5" v-if="!single && selectedActivity && ['0', '7', '6'].includes(selectedActivity.activityStatus) && hasActivityOperationPermission(selectedActivity)">
|
||
<el-button icon="Edit" @click="handleUpdate()" v-hasPermi="['stgl:activity:edit']">修改</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5" v-if="!single && selectedActivity && selectedActivity.activityStatus === '0' && hasActivityOperationPermission(selectedActivity)">
|
||
<el-button type="success" icon="Upload" @click="handleSubmit()" v-hasPermi="['stgl:activity:edit']">提交</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5" v-if="!single && selectedActivity && selectedActivity.activityStatus === '2' && hasActivityOperationPermission(selectedActivity)">
|
||
<el-button type="warning" icon="Check" @click="handleApprove()" v-hasPermi="['stgl:activity:approve']">审批</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5" v-if="!single && selectedActivity && ['3', '4'].includes(selectedActivity.activityStatus) && hasActivityOperationPermission(selectedActivity)">
|
||
<el-button type="danger" icon="Close" @click="handleCancel()" v-hasPermi="['stgl:activity:edit']">取消活动</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button icon="CopyDocument" :disabled="single" @click="handleCopy()" v-hasPermi="['stgl:activity:add']">复制</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button
|
||
icon="Delete"
|
||
:disabled="multiple || !['0', '7'].includes(selectedActivity?.activityStatus || '')"
|
||
@click="handleDelete()"
|
||
v-hasPermi="['stgl:activity:remove']"
|
||
:title="selectedActivity && !['0', '7'].includes(selectedActivity.activityStatus) ? '只有草稿和已驳回状态的活动可以删除' : ''"
|
||
>删除</el-button
|
||
>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="success" icon="Download" @click="handleExport" v-hasPermi="['stgl:activity:export']">导出</el-button>
|
||
</el-col>
|
||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||
</el-row>
|
||
</template>
|
||
|
||
<el-table v-loading="loading" border :data="activityList" @selection-change="handleSelectionChange" @row-dblclick="handleView">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="活动名称" align="center" prop="activityName" min-width="150" show-overflow-tooltip />
|
||
<el-table-column label="活动类型" align="center" prop="activityType" width="100">
|
||
<template #default="scope">
|
||
<dict-tag :options="stgl_activity_type" :value="scope.row.activityType" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="主办方" align="center" prop="organizerName" width="120" show-overflow-tooltip />
|
||
<el-table-column label="关联社团/队伍" align="center" width="120" show-overflow-tooltip>
|
||
<template #default="scope">
|
||
<span v-if="scope.row.relatedClubName">{{ scope.row.relatedClubName }}</span>
|
||
<span v-else class="text-gray-400">未关联</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="指导老师" align="center" width="180" :show-overflow-tooltip="false">
|
||
<template #default="scope">
|
||
<div class="advisor-cell">
|
||
<span v-if="getAdvisorNames(scope.row).length === 0" class="no-advisor">暂无</span>
|
||
<div v-else class="advisor-list">
|
||
<el-tag v-for="(name, index) in getAdvisorNames(scope.row)" :key="index" type="info" size="small" class="advisor-tag" :title="name">
|
||
{{ name }}
|
||
</el-tag>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="活动时间" align="center" width="160">
|
||
<template #default="scope">
|
||
<div v-if="scope.row.startTime && scope.row.endTime">
|
||
<div>{{ formatDate(scope.row.startTime) }}</div>
|
||
<div class="text-sm text-gray-500">{{ formatDate(scope.row.endTime) }}</div>
|
||
</div>
|
||
<span v-else class="text-gray-400">未设置</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="报名时间" align="center" width="160">
|
||
<template #default="scope">
|
||
<div v-if="scope.row.registrationStart && scope.row.registrationEnd">
|
||
<div>{{ formatDate(scope.row.registrationStart) }}</div>
|
||
<div class="text-sm text-gray-500">{{ formatDate(scope.row.registrationEnd) }}</div>
|
||
</div>
|
||
<span v-else class="text-gray-400">未设置</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="活动状态" align="center" prop="activityStatus" width="100">
|
||
<template #default="scope">
|
||
<dict-tag :options="stgl_activity_status" :value="scope.row.activityStatus" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="报名范围" align="center" prop="activityRange" width="100">
|
||
<template #default="scope">
|
||
<dict-tag :options="stgl_activity_range" :value="scope.row.activityRange" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="报名状态" align="center" prop="registrationStatus" width="100">
|
||
<template #default="scope">
|
||
<dict-tag :options="stgl_registration_status" :value="scope.row.registrationStatus" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="报名人数" align="center" width="100">
|
||
<template #default="scope">
|
||
<el-tag type="info" size="small">{{ scope.row.registrationCount || 0 }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="平均评分" align="center" width="100">
|
||
<template #default="scope">
|
||
<div v-if="scope.row.averageRating" class="rating-cell">
|
||
<el-rate :model-value="scope.row.averageRating" disabled size="small" />
|
||
<span class="rating-text">{{ scope.row.averageRating }}</span>
|
||
</div>
|
||
<span v-else class="text-gray-400">暂无评分</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="是否已办" align="center" prop="isFinished" width="100">
|
||
<template #default="scope">
|
||
<dict-tag :options="sys_yes_no" :value="scope.row.isFinished" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建者" align="center" prop="createByName" width="100" />
|
||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="320" fixed="right">
|
||
<template #default="scope">
|
||
<el-tooltip content="查看" placement="top">
|
||
<el-button
|
||
link
|
||
type="primary"
|
||
icon="View"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px; color: blue"
|
||
@click="handleView(scope.row)"
|
||
v-hasPermi="['stgl:activity:query']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="修改" placement="top" v-if="['0', '7', '6'].includes(scope.row.activityStatus) && hasActivityOperationPermission(scope.row)">
|
||
<el-button
|
||
link
|
||
type="primary"
|
||
icon="Edit"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleUpdate(scope.row)"
|
||
v-hasPermi="['stgl:activity:edit']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="提交" placement="top" v-if="scope.row.activityStatus === '0' && hasActivityOperationPermission(scope.row)">
|
||
<el-button
|
||
link
|
||
type="success"
|
||
icon="Upload"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleSubmit(scope.row)"
|
||
v-hasPermi="['stgl:activity:edit']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="审批" placement="top" v-if="scope.row.activityStatus === '2' && hasActivityOperationPermission(scope.row)">
|
||
<el-button
|
||
link
|
||
type="warning"
|
||
icon="Check"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleApprove(scope.row)"
|
||
v-hasPermi="['stgl:activity:approve']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="取消活动" placement="top" v-if="['3', '4'].includes(scope.row.activityStatus) && hasActivityOperationPermission(scope.row)">
|
||
<el-button
|
||
link
|
||
type="danger"
|
||
icon="Close"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleCancel(scope.row)"
|
||
v-hasPermi="['stgl:activity:edit']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="复制" placement="top">
|
||
<el-button
|
||
link
|
||
type="warning"
|
||
icon="CopyDocument"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleCopy(scope.row)"
|
||
v-hasPermi="['stgl:activity:add']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="活动总结" placement="top" v-if="shouldShowSummaryButton(scope.row) && hasActivityOperationPermission(scope.row)">
|
||
<el-button
|
||
link
|
||
type="success"
|
||
icon="Document"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleSummary(scope.row)"
|
||
v-hasPermi="['stgl:activity:edit']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="报名管理" placement="top">
|
||
<el-button
|
||
link
|
||
type="info"
|
||
icon="User"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleMembersReview(scope.row)"
|
||
v-hasPermi="['stgl:activity:query']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="删除" placement="top" v-if="['0', '7'].includes(scope.row.activityStatus) && hasActivityOperationPermission(scope.row)">
|
||
<el-button
|
||
link
|
||
type="danger"
|
||
icon="Delete"
|
||
size="default"
|
||
style="font-size: 16px"
|
||
@click="handleDelete(scope.row)"
|
||
v-hasPermi="['stgl:activity:remove']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<div class="pagination-container">
|
||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 添加或修改活动信息对话框 -->
|
||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1400px" append-to-body>
|
||
<el-form ref="activityFormRef" :model="form" :rules="rules" label-width="120px">
|
||
<el-tabs v-model="activeTab" type="border-card">
|
||
<el-tab-pane label="基本信息" name="basic">
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="活动名称" prop="activityName" required>
|
||
<el-input v-model="form.activityName" placeholder="请输入活动名称" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="活动类型" prop="activityType" required>
|
||
<el-select v-model="form.activityType" placeholder="请选择活动类型" style="width: 100%">
|
||
<el-option v-for="dict in stgl_activity_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="活动主题" prop="activityTheme">
|
||
<el-input v-model="form.activityTheme" placeholder="请输入活动主题" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="活动地点" prop="location">
|
||
<el-input v-model="form.location" placeholder="请输入活动地点" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="开始时间" prop="startTime" required>
|
||
<el-date-picker
|
||
v-model="form.startTime"
|
||
type="datetime"
|
||
placeholder="请选择开始时间"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="结束时间" prop="endTime" required>
|
||
<el-date-picker
|
||
v-model="form.endTime"
|
||
type="datetime"
|
||
placeholder="请选择结束时间"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="关联社团/队伍" prop="relatedClubId">
|
||
<ClubSelector v-model="form.relatedClub" @change="handleRelatedClubChange" :disabled="isRelatedClubDisabled" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-form-item label="活动海报" prop="posterUrl">
|
||
<image-upload v-model="form.posterUrl" />
|
||
</el-form-item>
|
||
<el-form-item label="活动简介" prop="description">
|
||
<el-input v-model="form.description" type="textarea" placeholder="请输入活动简介" resize="none" :rows="4" />
|
||
</el-form-item>
|
||
|
||
<!-- 主办方信息 -->
|
||
<div class="section-divider">
|
||
<h4 class="section-title">主办方信息</h4>
|
||
</div>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="主办方名称" prop="organizerName" required>
|
||
<el-input v-model="form.organizerName" placeholder="请输入主办方名称" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="联系人" prop="organizerContact">
|
||
<el-input v-model="form.organizerContact" placeholder="请输入联系人姓名" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="联系电话" prop="organizerPhone">
|
||
<el-input v-model="form.organizerPhone" placeholder="请输入联系电话" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 承办方信息 -->
|
||
<div class="section-divider">
|
||
<h4 class="section-title">承办方信息</h4>
|
||
</div>
|
||
<CoOrganizerManager v-model="form.coOrganizerList" @change="handleCoOrganizersChange" />
|
||
|
||
<!-- 指导老师信息 -->
|
||
<div class="section-divider">
|
||
<h4 class="section-title">指导老师信息</h4>
|
||
</div>
|
||
<AdvisorManager
|
||
:key="`advisor-manager-${form.id || 'new'}-${formKey}`"
|
||
v-model="form.advisors"
|
||
@change="handleAdvisorsChange"
|
||
:initial-advisors="form.advisors"
|
||
/>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="报名设置" name="registration">
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="报名方式" prop="registrationType" required>
|
||
<el-select v-model="form.registrationType" placeholder="请选择报名方式" style="width: 100%" @change="handleRegistrationTypeChange">
|
||
<el-option v-for="dict in stgl_registration_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="是否需要审核" prop="needApproval">
|
||
<el-select v-model="form.needApproval" placeholder="请选择是否需要审核" style="width: 100%">
|
||
<el-option label="否" :value="0" />
|
||
<el-option label="是" :value="1" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="报名范围" prop="activityRange" required>
|
||
<el-radio-group v-model="form.activityRange" style="width: 100%">
|
||
<el-radio v-for="dict in stgl_activity_range" :key="dict.value" :value="Number(dict.value)">
|
||
{{ dict.label }}
|
||
</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="报名开始时间" prop="registrationStart">
|
||
<el-date-picker
|
||
v-model="form.registrationStart"
|
||
type="datetime"
|
||
placeholder="请选择报名开始时间"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="报名结束时间" prop="registrationEnd">
|
||
<el-date-picker
|
||
v-model="form.registrationEnd"
|
||
type="datetime"
|
||
placeholder="请选择报名结束时间"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20" v-if="form.registrationType === '1' || form.registrationType === '3'">
|
||
<el-col :span="12">
|
||
<el-form-item label="总人数上限" prop="maxParticipants">
|
||
<el-input-number v-model="form.maxParticipants" :min="1" :max="9999" placeholder="请输入总人数上限" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20" v-if="form.registrationType === '2' || form.registrationType === '3'">
|
||
<el-col :span="8">
|
||
<el-form-item label="团队数量上限" prop="maxTeams">
|
||
<el-input-number v-model="form.maxTeams" :min="1" :max="9999" placeholder="请输入团队数量上限" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="每队最小人数" prop="teamSizeMin">
|
||
<el-input-number v-model="form.teamSizeMin" :min="1" :max="99" placeholder="请输入每队最小人数" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="每队最大人数" prop="teamSizeMax">
|
||
<el-input-number v-model="form.teamSizeMax" :min="1" :max="99" placeholder="请输入每队最大人数" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-tab-pane>
|
||
|
||
|
||
|
||
|
||
</el-tabs>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button :loading="buttonLoading" plain type="primary" @click="submitForm">确 定</el-button>
|
||
<el-button @click="cancel">取 消</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 查看活动详细信息对话框 -->
|
||
<el-dialog :title="viewDialog.title" v-model="viewDialog.visible" width="1200px" append-to-body>
|
||
<ActivityDetailView :activity="viewForm" />
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="viewDialog.visible = false">关 闭</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 活动复制对话框 -->
|
||
<el-dialog v-model="copyDialog.visible" title="复制活动" width="500px" append-to-body>
|
||
<el-form ref="copyFormRef" :model="copyForm" :rules="copyRules" label-width="120px">
|
||
<el-form-item label="源活动名称">
|
||
<el-input v-model="copyForm.sourceActivityName" readonly />
|
||
</el-form-item>
|
||
<el-form-item label="新活动名称" prop="newActivityName">
|
||
<el-input v-model="copyForm.newActivityName" placeholder="请输入新活动名称" maxlength="100" show-word-limit />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="copyDialog.visible = false">取消</el-button>
|
||
<el-button type="primary" @click="confirmCopy" :loading="copyLoading">确认复制</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 活动总结对话框 -->
|
||
<el-dialog v-model="summaryDialog.visible" title="活动总结" width="900px" append-to-body>
|
||
<el-form ref="summaryFormRef" :model="summaryForm" label-width="120px">
|
||
<el-form-item label="活动名称">
|
||
<el-input v-model="summaryForm.activityName" readonly />
|
||
</el-form-item>
|
||
<el-form-item label="活动总结报告" prop="summary">
|
||
<el-input v-model="summaryForm.summary" type="textarea" placeholder="请输入活动总结报告" resize="none" :rows="6" />
|
||
</el-form-item>
|
||
<el-form-item label="总结报告附件" prop="summaryAttachments">
|
||
<file-upload v-model="summaryForm.summaryAttachments" />
|
||
</el-form-item>
|
||
<el-form-item label="活动成果" prop="achievements">
|
||
<el-input v-model="summaryForm.achievements" type="textarea" placeholder="请输入活动成果" resize="none" :rows="6" />
|
||
</el-form-item>
|
||
<el-form-item label="成果附件" prop="achievementsAttachments">
|
||
<file-upload v-model="summaryForm.achievementsAttachments" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="summaryDialog.visible = false">取消</el-button>
|
||
<el-button type="primary" @click="confirmSummary" :loading="summaryLoading">确认总结</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 成员审核弹窗 -->
|
||
<ActivityMembersDialog
|
||
v-model:visible="membersDialog.visible"
|
||
:activity-id="selectedActivity?.id"
|
||
:activity-name="selectedActivity?.activityName"
|
||
ref="membersDialogRef"
|
||
/>
|
||
|
||
<!-- 审批对话框 -->
|
||
<el-dialog v-model="approveDialog.visible" title="审批活动" width="500px" append-to-body>
|
||
<el-form ref="approveFormRef" :model="approveForm" label-width="100px">
|
||
<el-form-item label="活动名称">
|
||
<el-input v-model="approveForm.activityName" readonly />
|
||
</el-form-item>
|
||
<el-form-item label="审批结果" prop="approvalResult">
|
||
<el-radio-group v-model="approveForm.approvalResult">
|
||
<el-radio label="approve">通过</el-radio>
|
||
<el-radio label="reject">驳回</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="驳回原因" prop="rejectReason" v-if="approveForm.approvalResult === 'reject'">
|
||
<el-input
|
||
v-model="approveForm.rejectReason"
|
||
type="textarea"
|
||
placeholder="请输入驳回原因"
|
||
:rows="3"
|
||
:maxlength="200"
|
||
show-word-limit
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="approveDialog.visible = false">取消</el-button>
|
||
<el-button type="primary" @click="confirmApprove" :loading="approveLoading">确认审批</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="StglActivity" lang="ts">
|
||
import { nextTick } from 'vue';
|
||
import { useRoute, useRouter } from 'vue-router';
|
||
import { ElNotification } from 'element-plus';
|
||
import {
|
||
listStglActivity,
|
||
getStglActivity,
|
||
delStglActivity,
|
||
addStglActivity,
|
||
updateStglActivity,
|
||
exportStglActivity,
|
||
copyStglActivity,
|
||
summarizeActivity
|
||
} from '@/api/stgl/activity';
|
||
import { StglActivityVO, StglActivityQuery, StglActivityForm, ClubOption, CoOrganizerInfo, InstructorInfo } from '@/api/stgl/activity/types';
|
||
import ClubSelector from '@/components/ClubSelector/index.vue';
|
||
import CoOrganizerManager from '@/components/CoOrganizerManager/index.vue';
|
||
import InstructorManager from '@/components/InstructorManager/index.vue';
|
||
import AdvisorManager from '@/components/AdvisorManager/index.vue';
|
||
import ActivityRatingView from './components/ActivityRatingView.vue';
|
||
import ActivityDetailView from './components/ActivityDetailView.vue';
|
||
import ActivityMembersDialog from './components/ActivityMembersDialog.vue';
|
||
import ActivityRegistrationView from './components/ActivityRegistrationView.vue'; // Added import
|
||
import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
||
import { useUserStore } from '@/store/modules/user';
|
||
|
||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||
const route = useRoute();
|
||
const router = useRouter();
|
||
const userStore = useUserStore();
|
||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, stgl_activity_range, sys_yes_no } = toRefs<any>(
|
||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'stgl_activity_range', 'sys_yes_no')
|
||
);
|
||
|
||
const activityList = ref<StglActivityVO[]>([]);
|
||
const buttonLoading = ref(false);
|
||
const loading = ref(true);
|
||
const showSearch = ref(true);
|
||
const ids = ref<Array<string | number>>([]);
|
||
const single = ref(true);
|
||
const multiple = ref(true);
|
||
const total = ref(0);
|
||
const activeTab = ref('basic');
|
||
const dateRange = ref<[string, string]>();
|
||
const selectedActivity = ref<StglActivityVO | null>(null);
|
||
const formKey = ref(0);
|
||
|
||
const queryFormRef = ref<ElFormInstance>();
|
||
const activityFormRef = ref<ElFormInstance>();
|
||
const copyFormRef = ref<ElFormInstance>();
|
||
const summaryFormRef = ref<ElFormInstance>();
|
||
const approveFormRef = ref<ElFormInstance>();
|
||
const membersDialogRef = ref();
|
||
|
||
const dialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
const viewDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
const copyDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
const summaryDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
const membersDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
// 审批对话框
|
||
const approveDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
// 审批表单
|
||
const approveForm = reactive({
|
||
id: undefined as string | number | undefined,
|
||
activityName: '',
|
||
approvalResult: 'approve' as 'approve' | 'reject',
|
||
rejectReason: ''
|
||
});
|
||
|
||
const copyLoading = ref(false);
|
||
const summaryLoading = ref(false);
|
||
const approveLoading = ref(false);
|
||
|
||
// 关联社团禁用状态控制
|
||
const isRelatedClubDisabled = ref(false);
|
||
|
||
const initFormData: StglActivityForm = {
|
||
id: undefined,
|
||
activityName: undefined,
|
||
activityType: undefined,
|
||
activityTheme: undefined,
|
||
startTime: undefined,
|
||
endTime: undefined,
|
||
location: undefined,
|
||
description: undefined,
|
||
posterUrl: undefined,
|
||
relatedClubId: undefined,
|
||
relatedType: undefined,
|
||
relatedClub: null,
|
||
organizerName: undefined,
|
||
organizerType: undefined,
|
||
organizerContact: undefined,
|
||
organizerPhone: undefined,
|
||
coOrganizerList: [],
|
||
coOrganizers: undefined,
|
||
instructorList: [],
|
||
instructors: '[]',
|
||
advisors: [],
|
||
registrationType: undefined,
|
||
registrationStart: undefined,
|
||
registrationEnd: undefined,
|
||
registrationStatus: undefined,
|
||
maxParticipants: undefined,
|
||
maxTeams: undefined,
|
||
teamSizeMin: undefined,
|
||
teamSizeMax: undefined,
|
||
needApproval: 1,
|
||
activityRange: 1, // 默认为1-本组织成员
|
||
activityStatus: '0',
|
||
isFinished: 0,
|
||
summary: undefined,
|
||
achievements: undefined,
|
||
summaryAttachments: undefined,
|
||
achievementsAttachments: undefined,
|
||
importSource: undefined,
|
||
remark: undefined
|
||
};
|
||
|
||
const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedClub?: ClubOption | null }>>({
|
||
form: { ...initFormData },
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
activityName: undefined,
|
||
activityType: undefined,
|
||
activityTheme: undefined,
|
||
startTime: undefined,
|
||
endTime: undefined,
|
||
location: undefined,
|
||
relatedClubId: undefined,
|
||
relatedClub: null,
|
||
organizerName: undefined,
|
||
registrationType: undefined,
|
||
registrationStart: undefined,
|
||
registrationEnd: undefined,
|
||
registrationStatus: undefined,
|
||
activityStatus: undefined,
|
||
isFinished: undefined,
|
||
params: {}
|
||
},
|
||
rules: {
|
||
activityName: [
|
||
{ required: true, message: '活动名称不能为空', trigger: 'blur' },
|
||
{ min: 2, max: 100, message: '活动名称长度在2到100个字符', trigger: 'blur' }
|
||
],
|
||
activityType: [{ required: true, message: '请选择活动类型', trigger: 'change' }],
|
||
startTime: [{ required: true, message: '请选择活动开始时间', trigger: 'change' }],
|
||
endTime: [{ required: true, message: '请选择活动结束时间', trigger: 'change' }],
|
||
organizerName: [
|
||
{ required: true, message: '主办方名称不能为空', trigger: 'blur' },
|
||
{ min: 2, max: 50, message: '主办方名称长度在2到50个字符', trigger: 'blur' }
|
||
],
|
||
registrationType: [{ required: true, message: '请选择报名方式', trigger: 'change' }],
|
||
activityRange: [{ required: true, message: '请选择报名范围', trigger: 'change' }]
|
||
}
|
||
});
|
||
|
||
const { queryParams, form, rules } = toRefs(data);
|
||
|
||
const viewForm = ref<StglActivityVO>({} as StglActivityVO);
|
||
|
||
|
||
|
||
/** 初始化URL参数 */
|
||
const initUrlParams = () => {
|
||
const { relatedClubId, relatedClubName, relatedTeamId, relatedTeamName, from } = route.query;
|
||
|
||
if (relatedClubId && relatedClubName && from === 'club') {
|
||
// 设置社团查询参数
|
||
queryParams.value.relatedClubId = relatedClubId as string | number;
|
||
queryParams.value.relatedClub = {
|
||
relatedClubId: relatedClubId as string | number,
|
||
relatedClubName: relatedClubName as string,
|
||
relatedType: 1, // 社团类型
|
||
uniqueKey: `1-${relatedClubId}`
|
||
};
|
||
|
||
console.log('初始化URL参数 - 社团过滤:', {
|
||
relatedClubId,
|
||
relatedClubName,
|
||
queryParams: queryParams.value.relatedClub
|
||
});
|
||
} else if (relatedTeamId && relatedTeamName && from === 'team') {
|
||
// 设置志愿者队伍查询参数
|
||
queryParams.value.relatedClubId = relatedTeamId as string | number;
|
||
queryParams.value.relatedClub = {
|
||
relatedClubId: relatedTeamId as string | number,
|
||
relatedClubName: relatedTeamName as string,
|
||
relatedType: 2, // 志愿者队伍类型
|
||
uniqueKey: `2-${relatedTeamId}`
|
||
};
|
||
|
||
console.log('初始化URL参数 - 志愿者队伍过滤:', {
|
||
relatedTeamId,
|
||
relatedTeamName,
|
||
queryParams: queryParams.value.relatedClub
|
||
});
|
||
}
|
||
};
|
||
|
||
// 复制表单
|
||
const copyForm = reactive({
|
||
sourceId: undefined as string | number | undefined,
|
||
sourceActivityName: '',
|
||
newActivityName: ''
|
||
});
|
||
|
||
const copyRules = {
|
||
newActivityName: [
|
||
{ required: true, message: '请输入新活动名称', trigger: 'blur' },
|
||
{ min: 1, max: 100, message: '活动名称长度在1到100个字符', trigger: 'blur' }
|
||
]
|
||
};
|
||
|
||
// 总结表单
|
||
const summaryForm = reactive({
|
||
id: undefined as string | number | undefined,
|
||
activityName: '',
|
||
summary: '',
|
||
achievements: '',
|
||
summaryAttachments: '',
|
||
achievementsAttachments: ''
|
||
});
|
||
|
||
/** 查询活动信息列表 */
|
||
const getList = async () => {
|
||
loading.value = true;
|
||
|
||
// 处理日期范围
|
||
if (dateRange.value && dateRange.value.length === 2) {
|
||
queryParams.value.startTime = dateRange.value[0];
|
||
queryParams.value.endTime = dateRange.value[1];
|
||
} else {
|
||
queryParams.value.startTime = undefined;
|
||
queryParams.value.endTime = undefined;
|
||
}
|
||
|
||
// 如果用户是社长角色,只能查看自己创建的活动
|
||
if (proxy?.$auth.hasRole('stgl_president')) {
|
||
queryParams.value.createBy = userStore.userId;
|
||
}
|
||
|
||
try {
|
||
const res = await listStglActivity(queryParams.value);
|
||
activityList.value = res.rows;
|
||
total.value = res.total;
|
||
} catch (error) {
|
||
console.error('查询活动列表失败:', error);
|
||
proxy?.$modal.msgError('查询活动列表失败');
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
/** 取消按钮 */
|
||
const cancel = () => {
|
||
reset();
|
||
dialog.visible = false;
|
||
};
|
||
|
||
/** 表单重置 */
|
||
const reset = () => {
|
||
// 直接重置,避免使用nextTick导致的递归更新
|
||
form.value = { ...initFormData };
|
||
// 确保指导老师字段被正确初始化
|
||
form.value.advisors = [];
|
||
form.value.instructorList = [];
|
||
form.value.instructors = '[]';
|
||
|
||
// 重置关联社团禁用状态
|
||
isRelatedClubDisabled.value = false;
|
||
|
||
// 重置审批表单
|
||
approveForm.id = undefined;
|
||
approveForm.activityName = '';
|
||
approveForm.approvalResult = 'approve';
|
||
approveForm.rejectReason = '';
|
||
|
||
// 更新formKey以强制重新渲染组件
|
||
formKey.value++;
|
||
|
||
activeTab.value = 'basic';
|
||
|
||
console.log('表单重置后的状态:', {
|
||
advisors: form.value.advisors,
|
||
instructorList: form.value.instructorList,
|
||
instructors: form.value.instructors
|
||
});
|
||
|
||
// 延迟重置表单字段,避免递归更新
|
||
setTimeout(() => {
|
||
activityFormRef.value?.resetFields();
|
||
}, 0);
|
||
};
|
||
|
||
/** 搜索按钮操作 */
|
||
const handleQuery = () => {
|
||
queryParams.value.pageNum = 1;
|
||
getList();
|
||
};
|
||
|
||
/** 重置按钮操作 */
|
||
const resetQuery = () => {
|
||
dateRange.value = undefined;
|
||
queryFormRef.value?.resetFields();
|
||
handleQuery();
|
||
};
|
||
|
||
/** 多选框选中数据 */
|
||
const handleSelectionChange = (selection: StglActivityVO[]) => {
|
||
ids.value = selection.map((item) => item.id);
|
||
single.value = selection.length != 1;
|
||
multiple.value = !selection.length;
|
||
selectedActivity.value = selection.length === 1 ? selection[0] : null;
|
||
};
|
||
|
||
/** 新增按钮操作 */
|
||
const handleAdd = () => {
|
||
// 先重置表单
|
||
form.value = { ...initFormData };
|
||
activeTab.value = 'basic';
|
||
|
||
// 确保指导老师字段被正确初始化
|
||
form.value.advisors = [];
|
||
form.value.instructorList = [];
|
||
form.value.instructors = '[]';
|
||
|
||
// 更新formKey以强制重新渲染组件
|
||
formKey.value++;
|
||
|
||
console.log('新增活动 - 初始化表单数据:', {
|
||
advisors: form.value.advisors,
|
||
instructorList: form.value.instructorList,
|
||
instructors: form.value.instructors,
|
||
formKey: formKey.value
|
||
});
|
||
|
||
// 如果有查询条件中的关联组织(社团或志愿者队伍),自动填充关联信息
|
||
if (queryParams.value.relatedClub) {
|
||
form.value.relatedClub = queryParams.value.relatedClub;
|
||
form.value.relatedClubId = queryParams.value.relatedClub.relatedClubId;
|
||
form.value.relatedType = queryParams.value.relatedClub.relatedType;
|
||
|
||
// 设置关联社团字段为禁用状态(不允许修改)
|
||
isRelatedClubDisabled.value = true;
|
||
|
||
console.log('新增活动 - 自动填充关联组织信息:', {
|
||
relatedType: queryParams.value.relatedClub.relatedType,
|
||
relatedClubId: queryParams.value.relatedClub.relatedClubId,
|
||
relatedClubName: queryParams.value.relatedClub.relatedClubName
|
||
});
|
||
|
||
// 确保表单数据正确设置
|
||
console.log('新增活动 - 表单数据设置后:', {
|
||
formRelatedClub: form.value.relatedClub,
|
||
formRelatedClubId: form.value.relatedClubId,
|
||
formRelatedType: form.value.relatedType
|
||
});
|
||
} else {
|
||
// 没有关联组织时,允许编辑关联社团字段
|
||
isRelatedClubDisabled.value = false;
|
||
}
|
||
|
||
dialog.visible = true;
|
||
dialog.title = '添加活动信息';
|
||
|
||
// 确保对话框完全打开后再检查表单状态
|
||
nextTick(() => {
|
||
console.log('新增活动 - 对话框打开后的表单状态:', {
|
||
advisors: form.value.advisors,
|
||
instructorList: form.value.instructorList,
|
||
instructors: form.value.instructors
|
||
});
|
||
});
|
||
};
|
||
|
||
/** 修改按钮操作 */
|
||
const handleUpdate = async (row?: StglActivityVO) => {
|
||
reset();
|
||
const _id = row?.id || ids.value[0];
|
||
|
||
// 先获取活动数据检查状态
|
||
const res = await getStglActivity(_id);
|
||
|
||
// 检查活动状态是否允许修改(只有草稿0、已驳回7、已取消6状态允许修改)
|
||
if (!['0', '7', '6'].includes(res.data.activityStatus)) {
|
||
const statusDict = stgl_activity_status.value.find((dict: any) => dict.value === res.data.activityStatus);
|
||
const statusName = statusDict ? statusDict.label : '当前状态';
|
||
proxy?.$modal.msgWarning(`活动状态为"${statusName}",不允许修改`);
|
||
return;
|
||
}
|
||
|
||
console.log('handleUpdate - 获取的活动数据:', res.data);
|
||
console.log('handleUpdate - 附件字段:', {
|
||
summaryAttachments: res.data.summaryAttachments,
|
||
achievementsAttachments: res.data.achievementsAttachments
|
||
});
|
||
|
||
// 处理关联社团数据
|
||
if (res.data.relatedClubId && res.data.relatedClubName) {
|
||
(res.data as any).relatedClub = {
|
||
relatedClubId: res.data.relatedClubId,
|
||
relatedClubName: res.data.relatedClubName,
|
||
relatedType: res.data.relatedType,
|
||
uniqueKey: `${res.data.relatedType}-${res.data.relatedClubId}`
|
||
};
|
||
|
||
// 修改时,如果是从URL参数进入的,则禁用关联社团字段
|
||
if (queryParams.value.relatedClub && queryParams.value.relatedClub.relatedClubId === res.data.relatedClubId) {
|
||
isRelatedClubDisabled.value = true;
|
||
} else {
|
||
isRelatedClubDisabled.value = false;
|
||
}
|
||
} else {
|
||
isRelatedClubDisabled.value = false;
|
||
}
|
||
|
||
// 处理承办方数据
|
||
if (res.data.coOrganizers) {
|
||
try {
|
||
(res.data as any).coOrganizerList = JSON.parse(res.data.coOrganizers);
|
||
} catch (error) {
|
||
(res.data as any).coOrganizerList = [];
|
||
}
|
||
} else {
|
||
(res.data as any).coOrganizerList = [];
|
||
}
|
||
|
||
// 处理指导老师数据
|
||
if (res.data.instructors) {
|
||
try {
|
||
(res.data as any).instructorList = JSON.parse(res.data.instructors);
|
||
} catch (error) {
|
||
(res.data as any).instructorList = [];
|
||
}
|
||
} else {
|
||
(res.data as any).instructorList = [];
|
||
}
|
||
|
||
// 处理AdvisorManager组件的指导老师数据
|
||
if (res.data.instructors) {
|
||
try {
|
||
const instructorsData = JSON.parse(res.data.instructors);
|
||
console.log('解析的指导老师数据:', instructorsData);
|
||
|
||
// 确保数据格式正确
|
||
if (Array.isArray(instructorsData)) {
|
||
(res.data as any).advisors = instructorsData.map((advisor: any) => ({
|
||
...advisor,
|
||
// 确保必要字段存在
|
||
name: advisor.name || advisor.teacher?.nickName || '',
|
||
dept: advisor.dept || advisor.teacher?.deptName || '',
|
||
phone: advisor.phone || advisor.teacher?.phonenumber || '',
|
||
email: advisor.email || advisor.teacher?.email || '',
|
||
userId: advisor.userId || advisor.teacher?.userId || ''
|
||
}));
|
||
} else {
|
||
(res.data as any).advisors = [];
|
||
}
|
||
} catch (error) {
|
||
console.warn('解析指导老师JSON数据失败:', error);
|
||
(res.data as any).advisors = [];
|
||
}
|
||
} else {
|
||
(res.data as any).advisors = [];
|
||
}
|
||
|
||
Object.assign(form.value, res.data);
|
||
|
||
// 确保数据类型正确
|
||
if (form.value.registrationType !== undefined) {
|
||
form.value.registrationType = String(form.value.registrationType);
|
||
}
|
||
|
||
// 确保指导老师数据正确设置
|
||
console.log('修改活动 - 设置表单数据后的状态:', {
|
||
advisors: form.value.advisors,
|
||
instructorList: form.value.instructorList,
|
||
instructors: form.value.instructors,
|
||
registrationType: form.value.registrationType
|
||
});
|
||
|
||
// 更新formKey以强制重新渲染组件
|
||
formKey.value++;
|
||
|
||
dialog.visible = true;
|
||
dialog.title = '修改活动信息';
|
||
};
|
||
|
||
/** 显示验证错误提示 */
|
||
const showValidationErrors = (invalidFields: any) => {
|
||
if (!invalidFields) return;
|
||
|
||
const errorMessages: string[] = [];
|
||
const tabMapping = {
|
||
// 基本信息标签页字段
|
||
'activityName': { tab: 'basic', label: '活动名称' },
|
||
'activityType': { tab: 'basic', label: '活动类型' },
|
||
'startTime': { tab: 'basic', label: '开始时间' },
|
||
'endTime': { tab: 'basic', label: '结束时间' },
|
||
'organizerName': { tab: 'basic', label: '主办方名称' },
|
||
// 报名设置标签页字段
|
||
'registrationType': { tab: 'registration', label: '报名方式' },
|
||
'activityRange': { tab: 'registration', label: '报名范围' }
|
||
};
|
||
|
||
let needSwitchTab = '';
|
||
let currentTabErrors: string[] = [];
|
||
let otherTabErrors: string[] = [];
|
||
|
||
// 分类错误信息
|
||
Object.keys(invalidFields).forEach(fieldName => {
|
||
const fieldInfo = tabMapping[fieldName];
|
||
if (fieldInfo) {
|
||
const errorMsg = invalidFields[fieldName][0]?.message || '验证失败';
|
||
// 去掉重复的"不能为空"等文字,只保留核心提示
|
||
const cleanMessage = errorMsg.replace(/不能为空$/, '').replace(/^请选择/, '').replace(/^请输入/, '');
|
||
const errorMessage = cleanMessage ? `${fieldInfo.label}:${cleanMessage}` : `${fieldInfo.label}为必填项`;
|
||
|
||
if (fieldInfo.tab === activeTab.value) {
|
||
currentTabErrors.push(errorMessage);
|
||
} else {
|
||
otherTabErrors.push(errorMessage);
|
||
if (!needSwitchTab) {
|
||
needSwitchTab = fieldInfo.tab;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// 构建提示消息
|
||
if (currentTabErrors.length > 0) {
|
||
errorMessages.push(`【当前页面】${currentTabErrors.join(';')}`);
|
||
}
|
||
|
||
if (otherTabErrors.length > 0) {
|
||
const tabName = needSwitchTab === 'registration' ? '报名设置' : '基本信息';
|
||
errorMessages.push(`【${tabName}页面】${otherTabErrors.join(';')}`);
|
||
}
|
||
|
||
if (errorMessages.length > 0) {
|
||
// 使用Notification在浏览器顶部显示提示
|
||
ElNotification({
|
||
title: '表单验证失败',
|
||
message: errorMessages.join('\n'),
|
||
type: 'warning',
|
||
duration: 10000,
|
||
position: 'top-right',
|
||
showClose: true
|
||
});
|
||
|
||
// 如果其他标签页有错误,自动切换到第一个有错误的标签页
|
||
if (needSwitchTab && needSwitchTab !== activeTab.value) {
|
||
activeTab.value = needSwitchTab;
|
||
|
||
// 切换标签页后延迟一下再次验证,确保用户能看到错误字段
|
||
nextTick(() => {
|
||
activityFormRef.value?.validate(() => {});
|
||
});
|
||
}
|
||
}
|
||
};
|
||
|
||
/** 提交按钮 */
|
||
const submitForm = () => {
|
||
activityFormRef.value?.validate(async (valid: boolean, invalidFields) => {
|
||
if (valid) {
|
||
buttonLoading.value = true;
|
||
|
||
// 准备提交的数据
|
||
const submitData = { ...form.value } as any;
|
||
|
||
console.log('提交前 - 原始表单数据:', {
|
||
advisors: submitData.advisors,
|
||
instructorList: submitData.instructorList,
|
||
instructors: submitData.instructors
|
||
});
|
||
|
||
// 新增时自动设置活动状态为0(草稿)
|
||
if (!submitData.id) {
|
||
submitData.activityStatus = '0';
|
||
}
|
||
|
||
// 处理关联社团数据
|
||
if (submitData.relatedClub) {
|
||
submitData.relatedClubId = submitData.relatedClub.relatedClubId;
|
||
submitData.relatedType = submitData.relatedClub.relatedType;
|
||
} else {
|
||
// 清空关联社团数据
|
||
submitData.relatedClubId = null;
|
||
submitData.relatedType = null;
|
||
}
|
||
|
||
// 调试信息
|
||
console.log('提交的关联社团数据:', {
|
||
relatedClub: submitData.relatedClub,
|
||
relatedClubId: submitData.relatedClubId,
|
||
relatedType: submitData.relatedType
|
||
});
|
||
|
||
// 处理承办方数据
|
||
if (submitData.coOrganizerList && submitData.coOrganizerList.length > 0) {
|
||
submitData.coOrganizers = JSON.stringify(submitData.coOrganizerList);
|
||
} else {
|
||
submitData.coOrganizers = '[]'; // 修复:使用空JSON数组而不是undefined
|
||
}
|
||
|
||
// 处理指导老师数据
|
||
if (submitData.advisors && submitData.advisors.length > 0) {
|
||
submitData.instructors = JSON.stringify(submitData.advisors);
|
||
} else {
|
||
submitData.instructors = '[]';
|
||
submitData.advisors = [];
|
||
}
|
||
|
||
try {
|
||
if (form.value.id) {
|
||
await updateStglActivity(submitData);
|
||
} else {
|
||
await addStglActivity(submitData);
|
||
}
|
||
proxy?.$modal.msgSuccess('操作成功');
|
||
dialog.visible = false;
|
||
|
||
// 提交成功后重置表单
|
||
reset();
|
||
|
||
await getList();
|
||
} catch (error) {
|
||
console.error('提交失败:', error);
|
||
} finally {
|
||
buttonLoading.value = false;
|
||
}
|
||
} else {
|
||
// 验证失败,显示必填项提示
|
||
showValidationErrors(invalidFields);
|
||
}
|
||
});
|
||
};
|
||
|
||
/** 删除按钮操作 */
|
||
const handleDelete = async (row?: StglActivityVO) => {
|
||
const _ids = row?.id || ids.value;
|
||
let confirmMessage = '';
|
||
|
||
// 检查删除权限 - 只有草稿(状态0)和已驳回(状态7)的活动可以删除
|
||
if (row) {
|
||
if (!['0', '7'].includes(row.activityStatus)) {
|
||
const statusDict = stgl_activity_status.value.find((dict: any) => dict.value === row.activityStatus);
|
||
const statusName = statusDict ? statusDict.label : '当前状态';
|
||
proxy?.$modal.msgWarning(`活动状态为"${statusName}",只有草稿和已驳回状态的活动可以删除`);
|
||
return;
|
||
}
|
||
confirmMessage = `是否确认删除活动"${row.activityName}"?`;
|
||
} else {
|
||
const selectedItems = activityList.value.filter((item) => ids.value.includes(item.id));
|
||
|
||
// 检查选中的活动是否都是草稿或已驳回状态
|
||
const nonDeletableItems = selectedItems.filter((item) => !['0', '7'].includes(item.activityStatus));
|
||
if (nonDeletableItems.length > 0) {
|
||
const names = nonDeletableItems.map((item) => item.activityName).join('、');
|
||
proxy?.$modal.msgWarning(`活动"${names}"不是草稿或已驳回状态,只有草稿和已驳回状态的活动可以删除`);
|
||
return;
|
||
}
|
||
|
||
if (selectedItems.length === 1) {
|
||
confirmMessage = `是否确认删除活动"${selectedItems[0].activityName}"?`;
|
||
} else {
|
||
const names = selectedItems.map((item) => item.activityName).join('、');
|
||
confirmMessage = `是否确认删除活动"${names}"?`;
|
||
}
|
||
}
|
||
|
||
await proxy?.$modal.confirm(confirmMessage);
|
||
await delStglActivity(_ids);
|
||
proxy?.$modal.msgSuccess('删除成功');
|
||
await getList();
|
||
};
|
||
|
||
/** 查看按钮操作 */
|
||
const handleView = async (row: StglActivityVO) => {
|
||
const res = await getStglActivity(row.id);
|
||
console.log('handleView - 获取的活动数据:', res.data);
|
||
console.log('handleView - 附件字段:', {
|
||
summaryAttachments: res.data.summaryAttachments,
|
||
achievementsAttachments: res.data.achievementsAttachments
|
||
});
|
||
viewForm.value = res.data;
|
||
viewDialog.visible = true;
|
||
viewDialog.title = '查看活动信息';
|
||
};
|
||
|
||
/** 复制按钮操作 */
|
||
const handleCopy = (row?: StglActivityVO) => {
|
||
const activity = row || activityList.value.find((item) => ids.value.includes(item.id));
|
||
if (!activity) return;
|
||
|
||
copyForm.sourceId = activity.id;
|
||
copyForm.sourceActivityName = activity.activityName || '';
|
||
copyForm.newActivityName = '';
|
||
copyDialog.visible = true;
|
||
};
|
||
|
||
/** 确认复制 */
|
||
const confirmCopy = async () => {
|
||
if (!copyFormRef.value) return;
|
||
|
||
await copyFormRef.value.validate(async (valid: boolean) => {
|
||
if (valid) {
|
||
copyLoading.value = true;
|
||
try {
|
||
await copyStglActivity({
|
||
sourceId: copyForm.sourceId!,
|
||
newActivityName: copyForm.newActivityName,
|
||
activityStatus: '0', // 复制后的活动状态设置为0(草稿)
|
||
registrationStatus: '0' // 复制后的报名状态设置为0(未开始)
|
||
});
|
||
proxy?.$modal.msgSuccess('复制成功');
|
||
copyDialog.visible = false;
|
||
await getList();
|
||
} catch (error) {
|
||
console.error('复制失败:', error);
|
||
} finally {
|
||
copyLoading.value = false;
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
/** 活动总结按钮操作 */
|
||
const handleSummary = async (row?: StglActivityVO) => {
|
||
const activity = row || activityList.value.find((item) => ids.value.includes(item.id));
|
||
if (!activity) return;
|
||
|
||
// 先重置总结表单
|
||
resetSummaryForm();
|
||
|
||
// 获取完整的活动数据,确保包含所有附件信息
|
||
const res = await getStglActivity(activity.id);
|
||
const fullActivity = res.data;
|
||
|
||
console.log('handleSummary - 完整活动数据:', fullActivity);
|
||
console.log('handleSummary - 附件字段:', {
|
||
summaryAttachments: fullActivity.summaryAttachments,
|
||
achievementsAttachments: fullActivity.achievementsAttachments
|
||
});
|
||
|
||
summaryForm.id = fullActivity.id;
|
||
summaryForm.activityName = fullActivity.activityName || '';
|
||
summaryForm.summary = fullActivity.summary || '';
|
||
summaryForm.achievements = fullActivity.achievements || '';
|
||
summaryForm.summaryAttachments = fullActivity.summaryAttachments || '';
|
||
summaryForm.achievementsAttachments = fullActivity.achievementsAttachments || '';
|
||
|
||
console.log('handleSummary - 设置到表单的数据:', {
|
||
summaryAttachments: summaryForm.summaryAttachments,
|
||
achievementsAttachments: summaryForm.achievementsAttachments
|
||
});
|
||
|
||
summaryDialog.visible = true;
|
||
};
|
||
|
||
/** 重置总结表单 */
|
||
const resetSummaryForm = () => {
|
||
summaryForm.id = undefined;
|
||
summaryForm.activityName = '';
|
||
summaryForm.summary = '';
|
||
summaryForm.achievements = '';
|
||
summaryForm.summaryAttachments = '';
|
||
summaryForm.achievementsAttachments = '';
|
||
};
|
||
|
||
/** 确认总结 */
|
||
const confirmSummary = async () => {
|
||
summaryLoading.value = true;
|
||
try {
|
||
console.log('confirmSummary - 提交的数据:', {
|
||
id: summaryForm.id,
|
||
summary: summaryForm.summary,
|
||
achievements: summaryForm.achievements,
|
||
summaryAttachments: summaryForm.summaryAttachments,
|
||
achievementsAttachments: summaryForm.achievementsAttachments
|
||
});
|
||
|
||
await summarizeActivity({
|
||
id: summaryForm.id!,
|
||
summary: summaryForm.summary,
|
||
achievements: summaryForm.achievements,
|
||
summaryAttachments: summaryForm.summaryAttachments,
|
||
achievementsAttachments: summaryForm.achievementsAttachments
|
||
});
|
||
proxy?.$modal.msgSuccess('活动总结成功');
|
||
summaryDialog.visible = false;
|
||
await getList();
|
||
} catch (error) {
|
||
console.error('活动总结失败:', error);
|
||
} finally {
|
||
summaryLoading.value = false;
|
||
}
|
||
};
|
||
|
||
/** 总结报告附件变化处理 */
|
||
const handleSummaryAttachmentsChange = (attachments: string) => {
|
||
summaryForm.summaryAttachments = attachments;
|
||
};
|
||
|
||
/** 活动成果附件变化处理 */
|
||
const handleAchievementsAttachmentsChange = (attachments: string) => {
|
||
summaryForm.achievementsAttachments = attachments;
|
||
};
|
||
|
||
/** 导出按钮操作 */
|
||
const handleExport = async () => {
|
||
try {
|
||
const res = await exportStglActivity(queryParams.value);
|
||
proxy?.$download.excel(res, '活动信息.xlsx');
|
||
} catch (error) {
|
||
console.error('导出失败:', error);
|
||
proxy?.$modal.msgError('导出失败');
|
||
}
|
||
};
|
||
|
||
/** 关联社团变化处理 */
|
||
const handleRelatedClubChange = (club: ClubOption | null) => {
|
||
console.log('活动表单 - handleRelatedClubChange:', club);
|
||
form.value.relatedClub = club;
|
||
form.value.relatedClubId = club?.relatedClubId;
|
||
form.value.relatedType = club?.relatedType; // 修复:同时设置关联类型
|
||
|
||
console.log('活动表单 - 更新后的表单数据:', {
|
||
relatedClub: form.value.relatedClub,
|
||
relatedClubId: form.value.relatedClubId,
|
||
relatedType: form.value.relatedType
|
||
});
|
||
};
|
||
|
||
/** 查询关联社团变化处理 */
|
||
const handleQueryRelatedClubChange = (club: ClubOption | null) => {
|
||
queryParams.value.relatedClub = club;
|
||
queryParams.value.relatedClubId = club?.relatedClubId;
|
||
};
|
||
|
||
|
||
|
||
/** 承办方变化处理 */
|
||
const handleCoOrganizersChange = (coOrganizers: CoOrganizerInfo[]) => {
|
||
form.value.coOrganizerList = coOrganizers;
|
||
};
|
||
|
||
/** 指导老师变化处理 */
|
||
const handleInstructorsChange = (instructors: InstructorInfo[]) => {
|
||
form.value.instructorList = instructors;
|
||
};
|
||
|
||
/** 指导老师变化处理 */
|
||
const handleAdvisorsChange = (advisors: AdvisorVO[]) => {
|
||
form.value.advisors = advisors;
|
||
};
|
||
|
||
/** 报名方式变化处理 */
|
||
const handleRegistrationTypeChange = (value: string) => {
|
||
// 根据报名方式重置相关字段
|
||
if (value === '1') {
|
||
// 个人报名
|
||
form.value.maxTeams = undefined;
|
||
form.value.teamSizeMin = undefined;
|
||
form.value.teamSizeMax = undefined;
|
||
} else if (value === '2') {
|
||
// 团队报名
|
||
form.value.maxParticipants = undefined;
|
||
}
|
||
};
|
||
|
||
/** 判断是否显示总结按钮 */
|
||
const shouldShowSummaryButton = (row: StglActivityVO): boolean => {
|
||
// 只有进行中(4)和已结束(5)状态的活动才显示总结按钮
|
||
return row.activityStatus === '4' || row.activityStatus === '5';
|
||
};
|
||
|
||
/** 成员审核按钮操作 */
|
||
const handleMembersReview = (row: StglActivityVO) => {
|
||
selectedActivity.value = row;
|
||
membersDialog.visible = true;
|
||
membersDialog.title = `${row.activityName}活动报名管理`;
|
||
};
|
||
|
||
/** 提交按钮操作 */
|
||
const handleSubmit = async (row?: StglActivityVO) => {
|
||
const activity = row || selectedActivity.value;
|
||
if (!activity) return;
|
||
|
||
try {
|
||
await proxy?.$modal.confirm(`是否确认提交活动"${activity.activityName}"?提交后将进入审批流程。`);
|
||
|
||
// 调用后端接口更新活动状态为2(待审批)
|
||
// 传递完整的活动数据,避免后端验证失败
|
||
await updateStglActivity({
|
||
...activity,
|
||
activityStatus: '2'
|
||
});
|
||
|
||
proxy?.$modal.msgSuccess('提交成功');
|
||
await getList();
|
||
} catch (error) {
|
||
if (error !== 'cancel') {
|
||
console.error('提交失败:', error);
|
||
proxy?.$modal.msgError('提交失败');
|
||
}
|
||
}
|
||
};
|
||
|
||
/** 审批按钮操作 */
|
||
const handleApprove = async (row?: StglActivityVO) => {
|
||
const activity = row || selectedActivity.value;
|
||
if (!activity) return;
|
||
|
||
// 显示审批对话框
|
||
approveDialog.visible = true;
|
||
approveDialog.title = `审批活动 - ${activity.activityName}`;
|
||
approveForm.id = activity.id;
|
||
approveForm.activityName = activity.activityName || '';
|
||
approveForm.approvalResult = 'approve';
|
||
approveForm.rejectReason = '';
|
||
};
|
||
|
||
/** 取消活动按钮操作 */
|
||
const handleCancel = async (row?: StglActivityVO) => {
|
||
const activity = row || selectedActivity.value;
|
||
if (!activity) return;
|
||
|
||
try {
|
||
await proxy?.$modal.confirm(`是否确认取消活动"${activity.activityName}"?取消后将无法恢复。`);
|
||
|
||
// 调用后端接口更新活动状态为6(已取消)
|
||
// 传递完整的活动数据,避免后端验证失败
|
||
await updateStglActivity({
|
||
...activity,
|
||
activityStatus: '6'
|
||
});
|
||
|
||
proxy?.$modal.msgSuccess('取消活动成功');
|
||
await getList();
|
||
} catch (error) {
|
||
if (error !== 'cancel') {
|
||
console.error('取消活动失败:', error);
|
||
proxy?.$modal.msgError('取消活动失败');
|
||
}
|
||
}
|
||
};
|
||
|
||
/** 确认审批 */
|
||
const confirmApprove = async () => {
|
||
if (!approveFormRef.value) return;
|
||
|
||
approveLoading.value = true;
|
||
try {
|
||
const activity = activityList.value.find(item => item.id === approveForm.id);
|
||
if (!activity) {
|
||
proxy?.$modal.msgError('活动信息不存在');
|
||
return;
|
||
}
|
||
|
||
let newStatus: string;
|
||
let successMessage: string;
|
||
|
||
if (approveForm.approvalResult === 'approve') {
|
||
// 审批通过,根据时间比较确定状态
|
||
const now = new Date();
|
||
const startTime = new Date(activity.startTime);
|
||
const endTime = new Date(activity.endTime);
|
||
|
||
if (now < startTime) {
|
||
newStatus = '3'; // 已通过(活动还未开始)
|
||
} else if (now >= startTime && now <= endTime) {
|
||
newStatus = '4'; // 进行中(活动正在进行)
|
||
} else {
|
||
newStatus = '5'; // 已结束(活动已结束)
|
||
}
|
||
successMessage = '审批通过';
|
||
} else {
|
||
// 审批驳回
|
||
if (!approveForm.rejectReason.trim()) {
|
||
proxy?.$modal.msgWarning('请输入驳回原因');
|
||
return;
|
||
}
|
||
newStatus = '7'; // 已驳回
|
||
successMessage = '审批驳回';
|
||
}
|
||
|
||
// 调用后端接口更新活动状态
|
||
// 传递完整的活动数据,避免后端验证失败
|
||
await updateStglActivity({
|
||
...activity,
|
||
activityStatus: newStatus
|
||
});
|
||
|
||
proxy?.$modal.msgSuccess(successMessage);
|
||
approveDialog.visible = false;
|
||
await getList();
|
||
} catch (error) {
|
||
console.error('审批失败:', error);
|
||
proxy?.$modal.msgError('审批失败');
|
||
} finally {
|
||
approveLoading.value = false;
|
||
}
|
||
};
|
||
|
||
/** 格式化日期 */
|
||
const formatDate = (dateStr: string): string => {
|
||
if (!dateStr) return '';
|
||
const date = new Date(dateStr);
|
||
return date.toLocaleString('zh-CN', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
};
|
||
|
||
/** 获取指导老师姓名列表 */
|
||
const getAdvisorNames = (activity: StglActivityVO): string[] => {
|
||
// 优先从JSON字段读取指导老师数据
|
||
if (activity.instructors) {
|
||
try {
|
||
const instructors = JSON.parse(activity.instructors);
|
||
if (Array.isArray(instructors)) {
|
||
return instructors.map((instructor: any) => instructor.name || instructor.teacher?.nickName || '').filter((name) => name);
|
||
}
|
||
} catch (error) {
|
||
console.warn('解析指导老师JSON数据失败:', error);
|
||
}
|
||
}
|
||
|
||
console.log('getAdvisorNames: 没有找到指导老师数据,activity:', activity);
|
||
return [];
|
||
};
|
||
|
||
// 检查是否拥有指导老师或管理员权限
|
||
const hasInstructorPermission = computed(() => {
|
||
return proxy?.$auth.hasRoleOr(['stgl_instructor', 'stgl_stgl_admin', 'superadmin']);
|
||
});
|
||
|
||
// 检查当前用户是否为指定活动的指导老师
|
||
const isCurrentUserAdvisor = (activity: StglActivityVO): boolean => {
|
||
if (!activity.instructors) return false;
|
||
|
||
try {
|
||
const advisors = JSON.parse(activity.instructors);
|
||
if (Array.isArray(advisors)) {
|
||
return advisors.some((advisor: any) => {
|
||
// 支持两种数据结构:直接包含userId或嵌套在teacher对象中
|
||
const userId = advisor.userId || advisor.teacher?.userId;
|
||
return userId && userId.toString() === userStore.userId?.toString();
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.warn('解析活动指导老师数据失败:', error);
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
// 检查当前用户是否有活动操作权限(指导老师只能操作自己指导的活动)
|
||
const hasActivityOperationPermission = (activity: StglActivityVO): boolean => {
|
||
// 管理员有所有权限
|
||
if (proxy?.$auth.hasRole('admin') || proxy?.$auth.hasRole('common')) {
|
||
return true;
|
||
}
|
||
|
||
// 指导老师只能操作自己指导的活动
|
||
if (proxy?.$auth.hasRole('stgl_instructor')) {
|
||
return isCurrentUserAdvisor(activity);
|
||
}
|
||
|
||
// 社长只能操作自己创建的活动
|
||
if (proxy?.$auth.hasRole('stgl_president')) {
|
||
return activity.createBy === userStore.userId;
|
||
}
|
||
|
||
// 志愿者队长只能操作自己创建的活动
|
||
if (proxy?.$auth.hasRole('stgl_volunteer_captain')) {
|
||
return activity.createBy === userStore.userId;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
onMounted(() => {
|
||
initUrlParams();
|
||
getList();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 页面容器样式 */
|
||
.page-container {
|
||
padding: 8px;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
/* 表格卡片布局 */
|
||
.table-card {
|
||
margin-top: 0;
|
||
border-radius: 12px;
|
||
border: 1px solid #e4e7ed;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.table-card :deep(.el-card__body) {
|
||
padding: 0px 20px 20px 20px !important;
|
||
}
|
||
|
||
/* 移除按钮栏底部边框 */
|
||
.button-card :deep(.el-card__header) {
|
||
border-bottom: none !important;
|
||
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
|
||
padding: 16px 20px;
|
||
}
|
||
|
||
/* 分页容器样式 */
|
||
.pagination-container {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
padding: 16px 20px 0 0 !important;
|
||
background-color: #fff;
|
||
margin-top: 0px !important;
|
||
}
|
||
|
||
/* 搜索按钮容器样式 */
|
||
.search-btn-container {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-top: 10px;
|
||
|
||
.el-form-item {
|
||
margin-bottom: 0 !important;
|
||
}
|
||
}
|
||
|
||
/* 章节分隔样式 */
|
||
.section-divider {
|
||
margin: 24px 0 16px 0;
|
||
padding-top: 16px;
|
||
border-top: 1px solid #e4e7ed;
|
||
}
|
||
|
||
.section-title {
|
||
margin: 0 0 16px 0;
|
||
color: #303133;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
border-bottom: 2px solid #409eff;
|
||
padding-bottom: 8px;
|
||
}
|
||
|
||
/* 评分单元格样式 */
|
||
.rating-cell {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.rating-text {
|
||
font-size: 12px;
|
||
color: #606266;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 指导老师单元格样式 */
|
||
.advisor-cell {
|
||
padding: 4px 8px;
|
||
width: 100%;
|
||
|
||
.no-advisor {
|
||
color: #909399;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.advisor-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 4px 6px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.advisor-tag {
|
||
margin: 0;
|
||
font-size: 12px;
|
||
line-height: 1.3;
|
||
padding: 2px 6px;
|
||
border-radius: 2px;
|
||
white-space: nowrap;
|
||
overflow: visible;
|
||
text-overflow: unset;
|
||
flex-shrink: 0;
|
||
max-width: none;
|
||
background-color: #f0f2f5;
|
||
color: #606266;
|
||
border: 1px solid #dcdfe6;
|
||
}
|
||
}
|
||
|
||
|
||
</style>
|