|
|
@@ -0,0 +1,1056 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <!-- 统计卡片 -->
|
|
|
+ <el-row :gutter="16" class="stats-row">
|
|
|
+ <el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
|
|
|
+ <div class="stat-card total">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <el-icon><User /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-number">{{ statistics.total }}</div>
|
|
|
+ <div class="stat-label">走访总数</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
|
|
|
+ <div class="stat-card month">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <el-icon><Calendar /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-number">{{ statistics.monthCount }}</div>
|
|
|
+ <div class="stat-label">本月走访</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
|
|
|
+ <div class="stat-card completed">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <el-icon><CircleCheck /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-number">{{ statistics.completedCount }}</div>
|
|
|
+ <div class="stat-label">已完成</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
|
|
|
+ <div class="stat-card pending">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <el-icon><Clock /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-number">{{ statistics.pendingCount }}</div>
|
|
|
+ <div class="stat-label">待走访</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 搜索区域 -->
|
|
|
+ <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="studentName">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.studentName"
|
|
|
+ placeholder="请输入学生姓名或学号"
|
|
|
+ clearable
|
|
|
+ style="width: 100%"
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="实习单位" prop="companyName">
|
|
|
+ <el-input v-model="queryParams.companyName" placeholder="请输入实习单位名称" clearable style="width: 100%" @keyup.enter="handleQuery" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="走访人" prop="visitorName">
|
|
|
+ <el-input v-model="queryParams.visitorName" placeholder="请输入走访人姓名" clearable style="width: 100%" @keyup.enter="handleQuery" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="走访状态" prop="status">
|
|
|
+ <el-select v-model="queryParams.status" placeholder="请选择走访状态" clearable style="width: 100%">
|
|
|
+ <el-option v-for="dict in visitStatusOptions" :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="visitDateRange">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryParams.visitDateRange"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div 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-button type="info" plain icon="Search" @click="showSearch = !showSearch">
|
|
|
+ {{ showSearch ? '隐藏' : '显示' }}搜索
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
+
|
|
|
+ <!-- 操作按钮栏 -->
|
|
|
+ <el-card shadow="hover" class="button-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="card-header-vertical">
|
|
|
+ <div class="toolbar-title">班主任走访列表</div>
|
|
|
+ <div class="button-group">
|
|
|
+ <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipVisit:add']">新增走访</el-button>
|
|
|
+ <el-button type="success" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipVisit:export']">导出</el-button>
|
|
|
+ <el-button type="info" plain icon="Search" @click="showSearch = !showSearch">
|
|
|
+ {{ showSearch ? '隐藏' : '显示' }}搜索
|
|
|
+ </el-button>
|
|
|
+ <el-tooltip content="刷新" placement="top">
|
|
|
+ <el-button icon="Refresh" circle @click="getList" />
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 数据表格 -->
|
|
|
+ <el-card shadow="hover" class="table-card">
|
|
|
+ <div class="table-container">
|
|
|
+ <el-table
|
|
|
+ v-loading="loading"
|
|
|
+ :data="visitList"
|
|
|
+ row-key="visitId"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ highlight-current-row
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" width="50" align="center" />
|
|
|
+
|
|
|
+ <el-table-column label="学生信息" min-width="150" align="left">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="student-info">
|
|
|
+ <div class="student-name">{{ scope.row.studentName }}</div>
|
|
|
+ <div class="student-number">学号:{{ scope.row.studentNumber }}</div>
|
|
|
+ <div class="student-class">班级:{{ scope.row.className }}</div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="实习单位" prop="companyName" min-width="150" align="left" show-overflow-tooltip />
|
|
|
+
|
|
|
+ <el-table-column label="走访人" prop="visitorName" min-width="100" align="center" />
|
|
|
+
|
|
|
+ <el-table-column label="走访日期" prop="visitDate" min-width="120" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ parseTime(scope.row.visitDate, '{y}-{m}-{d}') }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ <el-table-column label="走访状态" prop="status" min-width="100" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <dict-tag :options="visitStatusOptions" :value="scope.row.status || ''" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="创建时间" prop="createTime" min-width="160" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ parseTime(scope.row.createTime) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="action-buttons">
|
|
|
+ <el-tooltip content="查看详情" placement="top">
|
|
|
+ <el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['zsjy:internshipVisit:query']" />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="编辑" placement="top">
|
|
|
+ <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:internshipVisit:edit']" />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="删除" placement="top">
|
|
|
+ <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:internshipVisit:remove']" />
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 分页组件 -->
|
|
|
+ <div class="pagination-container">
|
|
|
+ <pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 详情对话框 -->
|
|
|
+ <el-dialog title="走访详情" v-model="viewDialogVisible" width="800px" append-to-body>
|
|
|
+ <div v-if="currentRecord" class="detail-container">
|
|
|
+ <el-descriptions :column="2" border>
|
|
|
+ <el-descriptions-item label="学生姓名">{{ currentRecord.studentName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="学号">{{ currentRecord.studentNumber }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="班级">{{ currentRecord.className }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="实习单位">{{ currentRecord.companyName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="走访人">{{ currentRecord.visitorName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="走访日期">
|
|
|
+ {{ parseTime(currentRecord.visitDate, '{y}-{m}-{d}') }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="走访状态">
|
|
|
+ <dict-tag :options="visitStatusOptions" :value="currentRecord.status || ''" />
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="走访目的" :span="2">{{ currentRecord.visitPurpose || '-' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="企业情况" :span="2">
|
|
|
+ <div style="white-space: pre-wrap">{{ currentRecord.companySituation || '-' }}</div>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="学生表现" :span="2">
|
|
|
+ <div style="white-space: pre-wrap">{{ currentRecord.studentPerformance || '-' }}</div>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="发现问题" :span="2">
|
|
|
+ <div style="white-space: pre-wrap">{{ currentRecord.problemsFound || '-' }}</div>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="改进建议" :span="2">
|
|
|
+ <div style="white-space: pre-wrap">{{ currentRecord.suggestions || '-' }}</div>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="走访总结" :span="2">
|
|
|
+ <div style="white-space: pre-wrap">{{ currentRecord.visitSummary || '-' }}</div>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="备注" :span="2">
|
|
|
+ <div style="white-space: pre-wrap">{{ currentRecord.remark || '-' }}</div>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创建时间">
|
|
|
+ {{ parseTime(currentRecord.createTime) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="更新时间">
|
|
|
+ {{ parseTime(currentRecord.updateTime) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer-right">
|
|
|
+ <el-button @click="viewDialogVisible = false">关闭</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 新增/编辑对话框 -->
|
|
|
+ <el-dialog :title="formTitle" v-model="formDialogVisible" width="800px" append-to-body>
|
|
|
+ <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="学生" prop="studentId" required>
|
|
|
+ <el-select
|
|
|
+ v-model="form.studentId"
|
|
|
+ placeholder="请选择学生"
|
|
|
+ filterable
|
|
|
+ remote
|
|
|
+ :remote-method="searchStudents"
|
|
|
+ :loading="studentLoading"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleStudentChange"
|
|
|
+ @focus="handleStudentFocus"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="student in studentOptions"
|
|
|
+ :key="student.studentId"
|
|
|
+ :label="`${student.studentName}(${student.studentNo})- ${student.className}`"
|
|
|
+ :value="student.studentId"
|
|
|
+ />
|
|
|
+ <!-- 空数据提示 -->
|
|
|
+ <el-option
|
|
|
+ v-if="studentOptions.length === 0 && !studentLoading"
|
|
|
+ disabled
|
|
|
+ label="暂无学生数据"
|
|
|
+ value=""
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <!-- 提示信息 -->
|
|
|
+ <div class="form-item-tip">
|
|
|
+ 系统将自动显示您所管理班级的学生,支持输入姓名或学号搜索
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="走访人" prop="visitorName" required>
|
|
|
+ <el-input v-model="form.visitorName" placeholder="请输入走访人姓名" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="走访日期" prop="visitDate" required>
|
|
|
+ <el-date-picker v-model="form.visitDate" type="date" placeholder="请选择走访日期" value-format="YYYY-MM-DD" style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="走访状态" prop="status" required>
|
|
|
+ <el-select v-model="form.status" placeholder="请选择走访状态" style="width: 100%">
|
|
|
+ <el-option v-for="dict in visitStatusOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-form-item label="走访目的" prop="visitPurpose" required>
|
|
|
+ <el-input v-model="form.visitPurpose" placeholder="请输入走访目的" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="企业情况" prop="companySituation">
|
|
|
+ <el-input v-model="form.companySituation" type="textarea" :rows="3" placeholder="请输入企业基本情况" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="学生表现" prop="studentPerformance">
|
|
|
+ <el-input v-model="form.studentPerformance" type="textarea" :rows="3" placeholder="请输入学生在企业的表现情况" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="发现问题" prop="problemsFound">
|
|
|
+ <el-input v-model="form.problemsFound" type="textarea" :rows="3" placeholder="请输入发现的问题" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="改进建议" prop="suggestions">
|
|
|
+ <el-input v-model="form.suggestions" type="textarea" :rows="3" placeholder="请输入改进建议" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="走访总结" prop="visitSummary">
|
|
|
+ <el-input v-model="form.visitSummary" type="textarea" :rows="4" placeholder="请输入走访总结" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="现场照片" prop="visitImages">
|
|
|
+ <image-upload v-model="form.visitImages" :limit="10" :file-size="10" />
|
|
|
+ <div class="form-item-tip">支持上传jpg、png、gif格式,单张图片不超过2MB,总大小不超过10MB</div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注" prop="remark">
|
|
|
+ <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注信息" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer-right">
|
|
|
+ <el-button @click="formDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="submitForm" :loading="formLoading">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="InternshipVisit">
|
|
|
+import { ref, reactive, onMounted, computed, getCurrentInstance } from 'vue';
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import { parseTime } from '@/utils/ruoyi';
|
|
|
+import { getDicts } from '@/api/system/dict/data/index';
|
|
|
+import { useUserStore } from '@/store/modules/user';
|
|
|
+import {
|
|
|
+ listInternshipVisit,
|
|
|
+ getInternshipVisit,
|
|
|
+ addInternshipVisit,
|
|
|
+ updateInternshipVisit,
|
|
|
+ delInternshipVisit,
|
|
|
+ exportInternshipVisit
|
|
|
+} from '@/api/zsjy/internshipVisit';
|
|
|
+import { listInternshipInfo } from '@/api/zsjy/internship';
|
|
|
+import ImageUpload from '@/components/ImageUpload/index.vue';
|
|
|
+
|
|
|
+// 获取组件实例和store
|
|
|
+const { proxy } = getCurrentInstance() as any;
|
|
|
+const userStore = useUserStore();
|
|
|
+
|
|
|
+// 响应式数据
|
|
|
+const loading = ref(true);
|
|
|
+const showSearch = ref(true);
|
|
|
+const formLoading = ref(false);
|
|
|
+const studentLoading = ref(false);
|
|
|
+const ids = ref([]);
|
|
|
+const single = ref(true);
|
|
|
+const multiple = ref(true);
|
|
|
+const total = ref(0);
|
|
|
+const visitList = ref([]);
|
|
|
+const formTitle = ref('');
|
|
|
+const viewDialogVisible = ref(false);
|
|
|
+const formDialogVisible = ref(false);
|
|
|
+const currentRecord = ref(null);
|
|
|
+
|
|
|
+// 统计数据
|
|
|
+const statistics = ref({
|
|
|
+ total: 0,
|
|
|
+ monthCount: 0,
|
|
|
+ completedCount: 0,
|
|
|
+ pendingCount: 0
|
|
|
+});
|
|
|
+
|
|
|
+// 字典数据
|
|
|
+const visitStatusOptions = ref([]);
|
|
|
+const studentOptions = ref([]);
|
|
|
+
|
|
|
+// 查询参数
|
|
|
+const queryParams = reactive({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ studentName: undefined,
|
|
|
+ companyName: undefined,
|
|
|
+ visitorName: undefined,
|
|
|
+ status: undefined,
|
|
|
+ visitDateRange: [],
|
|
|
+ visitStartDate: undefined,
|
|
|
+ visitEndDate: undefined
|
|
|
+});
|
|
|
+
|
|
|
+// 表单数据
|
|
|
+const form = reactive({
|
|
|
+ visitId: undefined,
|
|
|
+ internshipId: undefined, // 实习ID
|
|
|
+ studentId: undefined,
|
|
|
+ studentName: undefined,
|
|
|
+ studentNumber: undefined,
|
|
|
+ className: undefined,
|
|
|
+ companyName: undefined,
|
|
|
+ visitorName: undefined,
|
|
|
+ visitDate: undefined,
|
|
|
+ status: '0', // 0-草稿(对应字典的planned)
|
|
|
+ visitPurpose: undefined,
|
|
|
+ companySituation: undefined,
|
|
|
+ studentPerformance: undefined,
|
|
|
+ problemsFound: undefined,
|
|
|
+ suggestions: undefined,
|
|
|
+ visitSummary: undefined,
|
|
|
+ visitImages: undefined,
|
|
|
+ remark: undefined
|
|
|
+});
|
|
|
+
|
|
|
+// 表单验证规则
|
|
|
+const formRules = {
|
|
|
+ studentId: [{ required: true, message: '请选择学生', trigger: 'change' }],
|
|
|
+ visitorName: [{ required: true, message: '请输入走访人姓名', trigger: 'blur' }],
|
|
|
+ visitDate: [{ required: true, message: '请选择走访日期', trigger: 'change' }],
|
|
|
+ visitPurpose: [{ required: true, message: '请输入走访目的', trigger: 'blur' }],
|
|
|
+ status: [{ required: true, message: '请选择走访状态', trigger: 'change' }]
|
|
|
+};
|
|
|
+
|
|
|
+// 引用
|
|
|
+const queryRef = ref();
|
|
|
+const formRef = ref();
|
|
|
+
|
|
|
+/** 查询走访记录列表 */
|
|
|
+function getList() {
|
|
|
+ loading.value = true;
|
|
|
+ const params = { ...queryParams } as any;
|
|
|
+ if (params.visitDateRange && params.visitDateRange.length === 2) {
|
|
|
+ params.visitStartDate = params.visitDateRange[0];
|
|
|
+ params.visitEndDate = params.visitDateRange[1];
|
|
|
+ }
|
|
|
+ delete params.visitDateRange;
|
|
|
+
|
|
|
+ listInternshipVisit(params).then((response) => {
|
|
|
+ console.log('API响应数据:', response); // 用于调试
|
|
|
+
|
|
|
+ // 健壮的响应处理逻辑
|
|
|
+ if (response && typeof response === 'object') {
|
|
|
+ // 情况1:直接的TableDataInfo格式 {total: number, rows: array}
|
|
|
+ if (response.rows !== undefined && response.total !== undefined) {
|
|
|
+ visitList.value = response.rows || [];
|
|
|
+ total.value = response.total || 0;
|
|
|
+ }
|
|
|
+ // 情况2:嵌套在data中的TableDataInfo格式 {data: {total: number, rows: array}}
|
|
|
+ else if (response.data && response.data.rows !== undefined && response.data.total !== undefined) {
|
|
|
+ visitList.value = response.data.rows || [];
|
|
|
+ total.value = response.data.total || 0;
|
|
|
+ }
|
|
|
+ // 情况3:直接返回数组格式
|
|
|
+ else if (Array.isArray(response)) {
|
|
|
+ visitList.value = response;
|
|
|
+ total.value = response.length;
|
|
|
+ }
|
|
|
+ // 情况4:嵌套的数组格式
|
|
|
+ else if (response.data && Array.isArray(response.data)) {
|
|
|
+ visitList.value = response.data;
|
|
|
+ total.value = response.data.length;
|
|
|
+ }
|
|
|
+ // 其他未知格式
|
|
|
+ else {
|
|
|
+ console.warn('未识别的API响应格式:', response);
|
|
|
+ visitList.value = [];
|
|
|
+ total.value = 0;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.warn('API响应不是对象类型:', response);
|
|
|
+ visitList.value = [];
|
|
|
+ total.value = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = false;
|
|
|
+ // 计算统计数据
|
|
|
+ calculateStatistics();
|
|
|
+ }).catch((error) => {
|
|
|
+ console.error('获取走访记录列表失败:', error);
|
|
|
+ visitList.value = [];
|
|
|
+ total.value = 0;
|
|
|
+ loading.value = false;
|
|
|
+ // 可以添加用户友好的错误提示
|
|
|
+ proxy?.$modal.msgError('获取数据失败,请稍后重试');
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 计算统计数据 */
|
|
|
+function calculateStatistics() {
|
|
|
+ const now = new Date();
|
|
|
+ const currentMonth = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0');
|
|
|
+
|
|
|
+ statistics.value = {
|
|
|
+ total: visitList.value.length,
|
|
|
+ monthCount: visitList.value.filter((item) => item.visitDate && item.visitDate.startsWith(currentMonth)).length,
|
|
|
+ completedCount: visitList.value.filter((item) => item.status === '2').length, // 2-已审核
|
|
|
+ pendingCount: visitList.value.filter((item) => item.status === '0').length // 0-草稿
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/** 搜索按钮操作 */
|
|
|
+function handleQuery() {
|
|
|
+ queryParams.pageNum = 1;
|
|
|
+ getList();
|
|
|
+}
|
|
|
+
|
|
|
+/** 重置按钮操作 */
|
|
|
+function resetQuery() {
|
|
|
+ queryRef.value.resetFields();
|
|
|
+ queryParams.visitDateRange = [];
|
|
|
+ handleQuery();
|
|
|
+}
|
|
|
+
|
|
|
+/** 多选框选中数据 */
|
|
|
+function handleSelectionChange(selection) {
|
|
|
+ ids.value = selection.map((item) => item.visitId);
|
|
|
+ single.value = selection.length !== 1;
|
|
|
+ multiple.value = !selection.length;
|
|
|
+}
|
|
|
+
|
|
|
+/** 新增按钮操作 */
|
|
|
+function handleAdd() {
|
|
|
+ reset();
|
|
|
+ formDialogVisible.value = true;
|
|
|
+ formTitle.value = '新增走访记录';
|
|
|
+ // 新增:自动加载当前班主任班级的学生列表
|
|
|
+ loadCurrentTeacherStudents();
|
|
|
+}
|
|
|
+
|
|
|
+/** 查看详情 */
|
|
|
+function handleView(row) {
|
|
|
+ currentRecord.value = { ...row };
|
|
|
+ viewDialogVisible.value = true;
|
|
|
+}
|
|
|
+
|
|
|
+/** 修改按钮操作 */
|
|
|
+function handleUpdate(row) {
|
|
|
+ reset();
|
|
|
+ const visitId = row.visitId;
|
|
|
+ getInternshipVisit(visitId).then((response) => {
|
|
|
+ Object.assign(form, response.data);
|
|
|
+ formDialogVisible.value = true;
|
|
|
+ formTitle.value = '修改走访记录';
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 提交按钮 */
|
|
|
+function submitForm() {
|
|
|
+ formRef.value.validate((valid) => {
|
|
|
+ if (valid) {
|
|
|
+ // 检查实习ID是否已设置
|
|
|
+ if (!form.internshipId) {
|
|
|
+ ElMessage.error('请先选择学生,系统将自动设置实习ID');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('提交表单数据:', form);
|
|
|
+ formLoading.value = true;
|
|
|
+
|
|
|
+ if (form.visitId != undefined) {
|
|
|
+ updateInternshipVisit(form)
|
|
|
+ .then((response) => {
|
|
|
+ ElMessage.success('修改成功');
|
|
|
+ formDialogVisible.value = false;
|
|
|
+ getList();
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ formLoading.value = false;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ addInternshipVisit(form)
|
|
|
+ .then((response) => {
|
|
|
+ ElMessage.success('新增成功');
|
|
|
+ formDialogVisible.value = false;
|
|
|
+ getList();
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ formLoading.value = false;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除按钮操作 */
|
|
|
+function handleDelete(row) {
|
|
|
+ const visitIds = row.visitId || ids.value;
|
|
|
+ ElMessageBox.confirm('是否确认删除走访记录编号为"' + visitIds + '"的数据项?')
|
|
|
+ .then(function () {
|
|
|
+ return delInternshipVisit(visitIds);
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ getList();
|
|
|
+ ElMessage.success('删除成功');
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+}
|
|
|
+
|
|
|
+/** 导出按钮操作 */
|
|
|
+function handleExport() {
|
|
|
+ const params = { ...queryParams } as any;
|
|
|
+ if (params.visitDateRange && params.visitDateRange.length === 2) {
|
|
|
+ params.visitStartDate = params.visitDateRange[0];
|
|
|
+ params.visitEndDate = params.visitDateRange[1];
|
|
|
+ }
|
|
|
+ delete params.visitDateRange;
|
|
|
+
|
|
|
+ ElMessageBox.confirm('是否确认导出所有走访记录数据项?')
|
|
|
+ .then(function () {
|
|
|
+ return exportInternshipVisit(params);
|
|
|
+ })
|
|
|
+ .then((response) => {
|
|
|
+ // 处理下载
|
|
|
+ const blob = new Blob([response.data]);
|
|
|
+ const url = window.URL.createObjectURL(blob);
|
|
|
+ const link = document.createElement('a');
|
|
|
+ link.href = url;
|
|
|
+ link.download = '班主任走访记录_' + parseTime(new Date(), '{y}{m}{d}{h}{i}{s}') + '.xlsx';
|
|
|
+ link.click();
|
|
|
+ window.URL.revokeObjectURL(url);
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+}
|
|
|
+
|
|
|
+/** 搜索学生 */
|
|
|
+function searchStudents(query) {
|
|
|
+ studentLoading.value = true;
|
|
|
+
|
|
|
+ // 获取当前登录用户的班主任ID
|
|
|
+ const teacherId = userStore.userId;
|
|
|
+
|
|
|
+ // 构建查询参数
|
|
|
+ const params: any = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ classTeacherId: teacherId // 始终传递班主任ID
|
|
|
+ };
|
|
|
+
|
|
|
+ // 如果有搜索关键词,添加搜索条件
|
|
|
+ if (query && query.trim() !== '') {
|
|
|
+ const trimmedQuery = query.trim();
|
|
|
+ // 支持按姓名或学号搜索
|
|
|
+ if (/^\d+$/.test(trimmedQuery)) {
|
|
|
+ // 纯数字,按学号搜索
|
|
|
+ params.studentNo = trimmedQuery;
|
|
|
+ } else {
|
|
|
+ // 包含字母或中文,按姓名搜索
|
|
|
+ params.studentName = trimmedQuery;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('搜索学生参数:', params);
|
|
|
+
|
|
|
+ listInternshipInfo(params)
|
|
|
+ .then((response) => {
|
|
|
+ if (response.rows && Array.isArray(response.rows)) {
|
|
|
+ studentOptions.value = response.rows;
|
|
|
+ console.log('搜索到学生数据:', response.rows.length, '条');
|
|
|
+ console.log('学生数据示例:', response.rows.slice(0, 3));
|
|
|
+ } else {
|
|
|
+ studentOptions.value = [];
|
|
|
+ console.warn('未搜索到学生数据');
|
|
|
+ }
|
|
|
+ studentLoading.value = false;
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ console.error('搜索学生失败:', error);
|
|
|
+ studentOptions.value = [];
|
|
|
+ studentLoading.value = false;
|
|
|
+ ElMessage.error('搜索学生失败,请稍后重试');
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 学生选择变化 */
|
|
|
+function handleStudentChange(studentId) {
|
|
|
+ const student = studentOptions.value.find((item) => item.studentId === studentId);
|
|
|
+ if (student) {
|
|
|
+ console.log('选择的学生完整数据:', student);
|
|
|
+ form.studentName = student.studentName;
|
|
|
+ form.studentNumber = student.studentNo;
|
|
|
+ form.className = student.className;
|
|
|
+ form.companyName = student.companyName;
|
|
|
+
|
|
|
+ // 尝试多种可能的实习ID字段名
|
|
|
+ if (student.internshipId) {
|
|
|
+ form.internshipId = student.internshipId;
|
|
|
+ } else if (student.id) {
|
|
|
+ form.internshipId = student.id;
|
|
|
+ } else if (student.internshipInfoId) {
|
|
|
+ form.internshipId = student.internshipInfoId;
|
|
|
+ } else {
|
|
|
+ console.warn('未找到实习ID字段,学生数据:', student);
|
|
|
+ form.internshipId = undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('设置后的表单数据:', {
|
|
|
+ studentId: form.studentId,
|
|
|
+ internshipId: form.internshipId,
|
|
|
+ studentName: form.studentName
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 学生选择器获得焦点时的处理 */
|
|
|
+function handleStudentFocus() {
|
|
|
+ // 如果当前没有学生数据,自动加载一次
|
|
|
+ if (studentOptions.value.length === 0) {
|
|
|
+ loadCurrentTeacherStudents();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 表单重置 */
|
|
|
+function reset() {
|
|
|
+ Object.assign(form, {
|
|
|
+ visitId: undefined,
|
|
|
+ internshipId: undefined, // 实习ID
|
|
|
+ studentId: undefined,
|
|
|
+ studentName: undefined,
|
|
|
+ studentNumber: undefined,
|
|
|
+ className: undefined,
|
|
|
+ companyName: undefined,
|
|
|
+ visitorName: undefined,
|
|
|
+ visitDate: undefined,
|
|
|
+ status: '0', // 0-草稿(对应字典的planned)
|
|
|
+ visitPurpose: undefined,
|
|
|
+ companySituation: undefined,
|
|
|
+ studentPerformance: undefined,
|
|
|
+ problemsFound: undefined,
|
|
|
+ suggestions: undefined,
|
|
|
+ visitSummary: undefined,
|
|
|
+ visitImages: undefined,
|
|
|
+ remark: undefined
|
|
|
+ });
|
|
|
+ formRef.value?.resetFields();
|
|
|
+}
|
|
|
+
|
|
|
+/** 初始化字典数据 */
|
|
|
+function initDictData() {
|
|
|
+ // 获取走访状态字典
|
|
|
+ getDicts('zsjy_visit_status').then((response) => {
|
|
|
+ visitStatusOptions.value = response.data.map(item => ({
|
|
|
+ label: item.dictLabel,
|
|
|
+ value: item.dictValue
|
|
|
+ }));
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/** 加载当前班主任班级的学生列表 */
|
|
|
+function loadCurrentTeacherStudents() {
|
|
|
+ const teacherId = userStore.userId;
|
|
|
+ if (teacherId) {
|
|
|
+ studentLoading.value = true;
|
|
|
+ console.log('开始加载班主任ID为', teacherId, '的学生列表');
|
|
|
+
|
|
|
+ listInternshipInfo({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 100, // 加载尽可能多的学生
|
|
|
+ classTeacherId: teacherId
|
|
|
+ })
|
|
|
+ .then((response) => {
|
|
|
+ console.log('API响应完整结构:', response);
|
|
|
+ console.log('response.data:', response.data);
|
|
|
+ console.log('response直接访问:', response);
|
|
|
+
|
|
|
+ // 根据msgmanage/index.vue的用法,直接使用response.rows
|
|
|
+ if (response.rows && Array.isArray(response.rows)) {
|
|
|
+ studentOptions.value = response.rows;
|
|
|
+ console.log('成功加载学生数据:', response.rows.length, '条');
|
|
|
+ console.log('学生数据示例:', response.rows.slice(0, 3));
|
|
|
+ } else {
|
|
|
+ studentOptions.value = [];
|
|
|
+ console.warn('未获取到学生数据,响应结构:', response);
|
|
|
+ console.warn('response.rows类型:', typeof response.rows);
|
|
|
+ }
|
|
|
+ studentLoading.value = false;
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ console.error('加载学生数据失败:', error);
|
|
|
+ studentOptions.value = [];
|
|
|
+ studentLoading.value = false;
|
|
|
+ ElMessage.error('加载学生数据失败,请稍后重试');
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ console.warn('未获取到班主任ID');
|
|
|
+ ElMessage.warning('未获取到班主任ID,无法加载学生列表');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 组件挂载
|
|
|
+onMounted(() => {
|
|
|
+ initDictData();
|
|
|
+ getList();
|
|
|
+ // 预加载当前班主任班级的学生列表
|
|
|
+ loadCurrentTeacherStudents();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 搜索按钮容器样式 */
|
|
|
+.search-btn-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 按钮卡片样式 */
|
|
|
+.button-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.form-item-tip {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ line-height: 1.4;
|
|
|
+ margin-top: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header-vertical {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.toolbar-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.button-group {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.table-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 统计卡片样式 */
|
|
|
+.stats-row {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-right: 16px;
|
|
|
+ font-size: 24px;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card.total .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card.month .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card.completed .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card.pending .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-content {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-number {
|
|
|
+ font-size: 28px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+/* 主卡片样式 */
|
|
|
+.main-card {
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.main-card .el-card__body {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 搜索容器样式 */
|
|
|
+.search-container {
|
|
|
+ background: #f8f9fa;
|
|
|
+ padding: 16px;
|
|
|
+ border-radius: 6px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 工具栏样式 */
|
|
|
+.toolbar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding: 0 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.toolbar-left {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.toolbar-right {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格容器样式 */
|
|
|
+.table-container {
|
|
|
+ border-radius: 6px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.el-table {
|
|
|
+ border-radius: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.el-table th {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ color: #606266;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.el-table .el-table__row:hover {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+}
|
|
|
+
|
|
|
+/* 分页容器样式 */
|
|
|
+.pagination-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ margin-top: 20px;
|
|
|
+ padding: 16px 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 学生信息样式 */
|
|
|
+.student-info {
|
|
|
+ .student-name {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .student-number,
|
|
|
+ .student-class {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ line-height: 1.2;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 操作按钮样式 */
|
|
|
+.action-buttons {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 对话框样式 */
|
|
|
+.dialog-footer-right {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 详情容器样式 */
|
|
|
+.detail-container {
|
|
|
+ padding: 8px 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式设计 */
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .stat-card {
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .toolbar {
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: stretch;
|
|
|
+ }
|
|
|
+
|
|
|
+ .toolbar-left,
|
|
|
+ .toolbar-right {
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-buttons {
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|