个人学习记录

This commit is contained in:
2025-11-17 16:07:30 +08:00
parent 6e9057f6ee
commit d2e554c715
9 changed files with 529 additions and 40 deletions

View File

@@ -1,7 +1,9 @@
package org.xyzh.api.study.record; package org.xyzh.api.study.record;
import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.dto.study.TbLearningRecord; import org.xyzh.common.dto.study.TbLearningRecord;
import org.xyzh.common.vo.LearningStatisticsDetailVO;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
@@ -129,4 +131,23 @@ public interface LearningRecordService {
* @since 2025-10-30 * @since 2025-10-30
*/ */
ResultDomain<Map<String, Object>> getStudyRecordsRankings(); ResultDomain<Map<String, Object>> getStudyRecordsRankings();
/**
* @description 获取时间范围内每天的学习时长
* @param startTime 开始时间
* @param endTime 结束时间
* @return ResultDomain<Map<String, Object>> 用户学习记录统计
* @author yslg
* @since 2025-10-30
*/
ResultDomain<Map<String, Object>> getUserRecordRange(String startTime, String endTime);
/**
* @description 获取用户学习记录统计
* @param learningRecord 学习记录
* @return ResultDomain<Map<String, Object>> 用户学习记录统计
* @author yslg
* @since 2025-10-30
*/
ResultDomain<LearningStatisticsDetailVO> getUserRecordRangePage(PageRequest<TbLearningRecord> learningRecord);
} }

View File

@@ -6,10 +6,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.xyzh.api.study.record.LearningRecordService; import org.xyzh.api.study.record.LearningRecordService;
import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.dto.study.TbLearningRecord; import org.xyzh.common.dto.study.TbLearningRecord;
import org.springframework.web.bind.annotation.PutMapping; import org.xyzh.common.vo.LearningStatisticsDetailVO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map; import java.util.Map;
@@ -92,4 +91,28 @@ public class LearningRecordController {
public ResultDomain<Map<String, Object>> getStudyRecordsRankings() { public ResultDomain<Map<String, Object>> getStudyRecordsRankings() {
return learningRecordService.getStudyRecordsRankings(); return learningRecordService.getStudyRecordsRankings();
} }
/**
* 获取用户学习记录统计
* @param startTime 开始时间
* @param endTime 结束时间
* @return 用户学习记录统计
*/
@GetMapping("/user/record/range")
public ResultDomain<Map<String, Object>> getUserRecordRange(@RequestParam("startTime") String startTime, @RequestParam("endTime") String endTime) {
return learningRecordService.getUserRecordRange(startTime, endTime);
}
/**
* 获取用户学习记录统计
* @param startTime 开始时间
* @param endTime 结束时间
* @return 用户学习记录统计
*/
@PostMapping("/user/record/range")
public ResultDomain<LearningStatisticsDetailVO> getUserRecordRangePage(@RequestBody PageRequest<TbLearningRecord> learningRecord) {
return learningRecordService.getUserRecordRangePage(learningRecord);
}
} }

View File

@@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param;
import org.xyzh.common.core.page.PageParam; import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.dto.study.TbLearningRecord; import org.xyzh.common.dto.study.TbLearningRecord;
import org.xyzh.common.dto.study.TbLearningTask; import org.xyzh.common.dto.study.TbLearningTask;
import org.xyzh.common.vo.LearningStatisticsDetailVO;
import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.common.vo.UserDeptRoleVO;
import java.util.List; import java.util.List;
@@ -138,7 +139,7 @@ public interface LearningRecordMapper extends BaseMapper<TbLearningRecord> {
* @author yslg * @author yslg
* @since 2025-10-15 * @since 2025-10-15
*/ */
List<TbLearningRecord> selectLearningRecordsPage(@Param("filter") TbLearningRecord filter, @Param("pageParam") PageParam pageParam); List<LearningStatisticsDetailVO> selectLearningRecordsPage(@Param("filter") TbLearningRecord filter, @Param("pageParam") PageParam pageParam);
/** /**
* @description 统计学习记录总数 * @description 统计学习记录总数
@@ -195,4 +196,16 @@ public interface LearningRecordMapper extends BaseMapper<TbLearningRecord> {
* @since 2025-10-30 * @since 2025-10-30
*/ */
List<Map<String, Object>> getWeeklyTaskCompletionRanking(@Param("userDeptRoles") List<UserDeptRoleVO> userDeptRoles); List<Map<String, Object>> getWeeklyTaskCompletionRanking(@Param("userDeptRoles") List<UserDeptRoleVO> userDeptRoles);
/**
* @description 获取用户时间范围内每天的学习时长
* @param userId 用户ID
* @param startTime 开始时间
* @param endTime 结束时间
* @return List<Map<String, Object>> 每天学习时长数据
* @author yslg
* @since 2025-11-17
*/
List<Map<String, Object>> getUserDailyDuration(@Param("userId") String userId, @Param("startTime") String startTime, @Param("endTime") String endTime);
} }

View File

@@ -13,11 +13,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.enums.TaskItemType; import org.xyzh.common.core.enums.TaskItemType;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.core.page.PageDomain;
import org.xyzh.common.dto.study.TbLearningRecord; import org.xyzh.common.dto.study.TbLearningRecord;
import org.xyzh.common.dto.study.TbLearningTask; import org.xyzh.common.dto.study.TbLearningTask;
import org.xyzh.common.dto.study.TbTaskItem; import org.xyzh.common.dto.study.TbTaskItem;
import org.xyzh.common.dto.study.TbTaskUser; import org.xyzh.common.dto.study.TbTaskUser;
import org.xyzh.common.dto.user.TbSysUser; import org.xyzh.common.dto.user.TbSysUser;
import org.xyzh.common.vo.LearningStatisticsDetailVO;
import org.xyzh.common.vo.TaskItemVO; import org.xyzh.common.vo.TaskItemVO;
import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.common.vo.UserDeptRoleVO;
import org.xyzh.api.study.record.LearningRecordService; import org.xyzh.api.study.record.LearningRecordService;
@@ -328,4 +332,71 @@ public class SCLearningRecordServiceImpl implements LearningRecordService {
} }
return resultDomain; return resultDomain;
} }
@Override
public ResultDomain<Map<String, Object>> getUserRecordRange(String startTime, String endTime) {
ResultDomain<Map<String, Object>> resultDomain = new ResultDomain<>();
try {
// 获取当前用户ID
String userId = LoginUtil.getCurrentUserId();
if (userId == null) {
resultDomain.fail("用户未登录");
return resultDomain;
}
// 查询时间范围内每天的学习时长
List<Map<String, Object>> dailyData = learningRecordMapper.getUserDailyDuration(userId, startTime, endTime);
Map<String, Object> chartData = new HashMap<>();
chartData.put("dailyDuration", dailyData);
resultDomain.success("获取学习时长统计成功", chartData);
} catch (Exception e) {
logger.error("获取学习时长统计失败", e);
resultDomain.fail("获取学习时长统计失败:" + e.getMessage());
}
return resultDomain;
}
@Override
public ResultDomain<LearningStatisticsDetailVO> getUserRecordRangePage(PageRequest<TbLearningRecord> pageRequest) {
ResultDomain<LearningStatisticsDetailVO> resultDomain = new ResultDomain<>();
try {
// 获取当前用户ID
String userId = LoginUtil.getCurrentUserId();
if (userId == null) {
resultDomain.fail("用户未登录");
return resultDomain;
}
TbLearningRecord filter = pageRequest.getFilter();
if (filter == null) {
filter = new TbLearningRecord();
}
PageParam pageParam = pageRequest.getPageParam();
if (pageParam == null) {
pageParam = new PageParam();
}
// 设置当前用户ID用于筛选
filter.setUserID(userId);
// 查询分页数据
List<LearningStatisticsDetailVO> list = learningRecordMapper.selectLearningRecordsPage(filter, pageParam);
long total = learningRecordMapper.countLearningRecords(filter);
// 构建分页结果
PageDomain<LearningStatisticsDetailVO> pageDomain = new PageDomain<>();
pageDomain.setDataList(list);
pageParam.setTotalElements((int) total);
pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize()));
pageDomain.setPageParam(pageParam);
resultDomain.success("查询学习记录成功", pageDomain);
} catch (Exception e) {
logger.error("查询学习记录失败", e);
resultDomain.fail("查询学习记录失败:" + e.getMessage());
}
return resultDomain;
}
} }

View File

@@ -156,6 +156,9 @@
<if test="updateTime != null"> <if test="updateTime != null">
update_time = #{updateTime}, update_time = #{updateTime},
</if> </if>
<if test="startTime != null and endTime != null">
create_time BETWEEN #{startTime} AND #{endTime}
</if>
</set> </set>
WHERE id = #{id} WHERE id = #{id}
</update> </update>
@@ -187,21 +190,87 @@
</foreach> </foreach>
</delete> </delete>
<!-- 分页查询学习记录 --> <!-- LearningStatisticsDetailVO结果映射 -->
<select id="selectLearningRecordsPage" resultMap="BaseResultMap"> <resultMap id="StatisticsDetailResultMap" type="org.xyzh.common.vo.LearningStatisticsDetailVO">
<result column="stat_date" property="statDate" jdbcType="TIMESTAMP"/>
<result column="resource_type" property="resourceType" jdbcType="INTEGER"/>
<result column="resource_type_name" property="resourceTypeName" jdbcType="VARCHAR"/>
<result column="resource_id" property="resourceID" jdbcType="VARCHAR"/>
<result column="resource_title" property="resourceTitle" jdbcType="VARCHAR"/>
<result column="course_id" property="courseID" jdbcType="VARCHAR"/>
<result column="course_name" property="courseName" jdbcType="VARCHAR"/>
<result column="chapter_id" property="chapterID" jdbcType="VARCHAR"/>
<result column="chapter_name" property="chapterName" jdbcType="VARCHAR"/>
<result column="total_duration" property="totalDuration" jdbcType="INTEGER"/>
<result column="total_duration_formatted" property="totalDurationFormatted" jdbcType="VARCHAR"/>
<result column="learn_count" property="learnCount" jdbcType="INTEGER"/>
<result column="is_complete" property="isComplete" jdbcType="BOOLEAN"/>
<result column="complete_time" property="completeTime" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 分页查询学习记录详情 -->
<select id="selectLearningRecordsPage" resultMap="StatisticsDetailResultMap">
SELECT SELECT
<include refid="Base_Column_List" /> COALESCE(lr.last_learn_time, lr.update_time, lr.create_time) AS stat_date,
FROM tb_learning_record lr.resource_type AS resource_type,
<include refid="Where_Clause" /> CASE lr.resource_type
ORDER BY last_learn_time DESC, create_time DESC WHEN 1 THEN '文章'
WHEN 2 THEN '课程'
WHEN 3 THEN '章节'
ELSE '未知'
END AS resource_type_name,
lr.resource_id AS resource_id,
CASE
WHEN lr.resource_type = 1 THEN COALESCE(r.title, CONCAT('文章-', lr.resource_id))
WHEN lr.resource_type = 2 THEN COALESCE(c.name, CONCAT('课程-', lr.course_id))
WHEN lr.resource_type = 3 THEN COALESCE(ch.name, CONCAT('章节-', lr.chapter_id))
ELSE '未知资源'
END AS resource_title,
COALESCE(lr.course_id, '') AS course_id,
COALESCE(c.name, '') AS course_name,
COALESCE(lr.chapter_id, '') AS chapter_id,
COALESCE(ch.name, '') AS chapter_name,
COALESCE(lr.duration, 0) AS total_duration,
'' AS total_duration_formatted,
1 AS learn_count,
COALESCE(lr.is_complete, 0) AS is_complete,
lr.complete_time AS complete_time
FROM tb_learning_record lr
LEFT JOIN tb_resource r ON lr.resource_type = 1 AND lr.resource_id = r.resource_id
LEFT JOIN tb_course c ON lr.resource_type = 2 AND lr.course_id = c.id
LEFT JOIN tb_course_chapter ch ON lr.resource_type = 3 AND lr.chapter_id = ch.id
<where>
<if test="filter.userID != null and filter.userID != ''">
AND lr.user_id = #{filter.userID}
</if>
<if test="filter.resourceType != null">
AND lr.resource_type = #{filter.resourceType}
</if>
<if test="filter.isComplete != null">
AND lr.is_complete = #{filter.isComplete}
</if>
</where>
ORDER BY lr.last_learn_time DESC, lr.create_time DESC
LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset} LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset}
</select> </select>
<!-- 统计学习记录总数 --> <!-- 统计学习记录总数 -->
<select id="countLearningRecords" resultType="long"> <select id="countLearningRecords" resultType="long">
SELECT COUNT(1) SELECT COUNT(1)
FROM tb_learning_record FROM tb_learning_record lr
<include refid="Where_Clause" /> <where>
<if test="filter != null">
<if test="filter.userID != null and filter.userID != ''">
AND lr.user_id = #{filter.userID}
</if>
<if test="filter.resourceType != null">
AND lr.resource_type = #{filter.resourceType}
</if>
<if test="filter.isComplete != null">
AND lr.is_complete = #{filter.isComplete}
</if>
</if>
</where>
</select> </select>
<!-- 获取本周课程和文章的总学习时长统计 --> <!-- 获取本周课程和文章的总学习时长统计 -->
@@ -362,4 +431,30 @@
<select id="getTaskStaticByTag"> <select id="getTaskStaticByTag">
</select> </select>
<!-- 获取用户时间范围内每天的学习时长 -->
<select id="getUserDailyDuration" resultType="java.util.Map">
WITH RECURSIVE date_range AS (
-- 生成起始日期
SELECT DATE(#{startTime}) AS date
UNION ALL
-- 递归生成后续日期
SELECT DATE_ADD(date, INTERVAL 1 DAY)
FROM date_range
WHERE date &lt; DATE(#{endTime})
)
SELECT
dr.date AS date,
DATE_FORMAT(dr.date, '%Y-%m-%d') AS statDate,
COALESCE(SUM(lr.duration), 0) AS duration,
COALESCE(SUM(lr.duration), 0) AS totalDuration,
COUNT(lr.id) AS count
FROM date_range dr
LEFT JOIN tb_learning_record lr
ON DATE(lr.update_time) = dr.date
AND lr.user_id = #{userId}
GROUP BY dr.date
ORDER BY dr.date ASC
</select>
</mapper> </mapper>

View File

@@ -92,4 +92,25 @@ export const learningRecordApi = {
const response = await api.get<any>('/study/records/statistics/rankings'); const response = await api.get<any>('/study/records/statistics/rankings');
return response.data; return response.data;
}, },
/**
* 获取用户时间范围内的学习时长统计(用于图表)
* @param startTime 开始时间
* @param endTime 结束时间
* @returns Promise<ResultDomain<any>> 每天学习时长数据
*/
async getUserRecordRange(startTime: string, endTime: string): Promise<ResultDomain<any>> {
const response = await api.get<any>(`/study/records/user/record/range?startTime=${startTime}&endTime=${endTime}`);
return response.data;
},
/**
* 分页查询用户学习记录详情
* @param pageRequest 分页请求参数
* @returns Promise<ResultDomain<any>> 分页记录数据
*/
async getUserRecordRangePage(pageRequest: any): Promise<ResultDomain<any>> {
const response = await api.post<any>('/study/records/user/record/range', pageRequest);
return response.data;
},
}; };

View File

@@ -7,26 +7,46 @@
end-placeholder="结束日期" @change="handleDateChange" /> end-placeholder="结束日期" @change="handleDateChange" />
</div> </div>
<div class="records-list"> <!-- 学习时长图表 -->
<div class="record-item" v-for="record in records" :key="record.id"> <div class="chart-container">
<div class="record-icon"> <h3>学习时长统计</h3>
<i :class="getRecordIcon(record.type)"></i> <div ref="chartRef" style="width: 100%; height: 300px;"></div>
</div> </div>
<div class="record-content">
<h3>{{ record.title }}</h3> <!-- 学习记录分页表格 -->
<p class="record-description">{{ record.description }}</p> <div class="records-table" v-loading="tableLoading">
<div class="record-meta"> <h3>学习记录明细</h3>
<span class="record-type">{{ record.typeName }}</span> <el-table :data="tableData" stripe style="width: 100%">
<span class="record-duration">学习时长{{ record.duration }}分钟</span> <el-table-column prop="resourceTitle" label="资源标题" width="200" />
<span class="record-date">{{ record.learnDate }}</span> <el-table-column prop="resourceTypeName" label="类型" width="100" />
</div> <el-table-column label="学习时长" width="120">
</div> <template #default="{ row }">
<div class="record-progress"> {{ formatDuration(row.totalDuration) }}
<div class="progress-circle" :class="`progress-${record.status}`"> </template>
<span>{{ record.progress }}%</span> </el-table-column>
</div> <el-table-column prop="learnCount" label="学习次数" width="100" />
</div> <el-table-column prop="statDate" label="统计日期" width="150">
</div> <template #default="{ row }">
{{ row.statDate ? new Date(row.statDate).toLocaleDateString('zh-CN') : '' }}
</template>
</el-table-column>
<el-table-column label="完成状态" width="100">
<template #default="{ row }">
<el-tag :type="row.isComplete ? 'success' : 'info'">{{ row.isComplete ? '已完成' : '学习中' }}</el-tag>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="totalElements"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
style="margin-top: 20px; justify-content: center"
/>
</div> </div>
</div> </div>
@@ -34,18 +54,203 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted, nextTick } from 'vue';
import { ElDatePicker } from 'element-plus'; import { ElDatePicker, ElMessage, ElTable, ElTableColumn, ElPagination, ElTag } from 'element-plus';
import { UserCenterLayout } from '@/views/user/user-center'; import { UserCenterLayout } from '@/views/user/user-center';
const dateRange = ref<[Date, Date] | null>(null); import { learningRecordApi } from '@/apis/study/learning-record';
const records = ref<any[]>([]); import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
defineOptions({
name: 'LearningRecordsView'
});
// 默认最近7天
const getDefaultDateRange = (): [Date, Date] => {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 7);
return [start, end];
};
const dateRange = ref<[Date, Date]>(getDefaultDateRange());
const loading = ref(false);
// 图表相关
const chartRef = ref<HTMLElement>();
let chartInstance: ECharts | null = null;
// 分页表格相关
const tableData = ref<any[]>([]);
const tableLoading = ref(false);
const currentPage = ref(1);
const pageSize = ref(10);
const totalElements = ref(0);
onMounted(() => { onMounted(() => {
// TODO: 加载学习记录 // 初始化图表
nextTick(() => {
if (chartRef.value) {
chartInstance = echarts.init(chartRef.value);
loadChartData();
}
});
loadTableData();
}); });
function handleDateChange() { function handleDateChange() {
// TODO: 根据日期筛选记录 // 日期变化时重新加载图表和表格
loadChartData();
loadTableData();
}
// 加载图表数据
async function loadChartData() {
if (!chartInstance) return;
try {
const [startDate, endDate] = dateRange.value;
const startTime = startDate.toISOString().split('T')[0];
const endTime = endDate.toISOString().split('T')[0];
console.log('📅 查询时间范围:', startTime, '至', endTime);
const result = await learningRecordApi.getUserRecordRange(startTime, endTime);
console.log('📊 图表数据返回:', result);
if (result.success && result.data) {
const dailyData = result.data.dailyDuration || [];
console.log('📈 每日数据:', dailyData);
const dates = dailyData.map((item: any) => item.statDate || item.date);
// 转换为分钟保留1位小数避免小时长被四舍五入为0
const durations = dailyData.map((item: any) => {
const seconds = item.duration || item.totalDuration || 0;
return Math.round(seconds / 60 * 10) / 10; // 保留1位小数
});
console.log('📆 日期数组:', dates);
console.log('⏱️ 时长数组(分钟):', durations);
const option = {
title: {
text: '每日学习时长(分钟)',
left: 'center'
},
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
const data = params[0];
const minutes = data.value;
if (minutes < 1) {
// 小于1分钟显示秒数
return `${data.axisValue}<br/>${data.seriesName}: ${Math.round(minutes * 60)}`;
}
return `${data.axisValue}<br/>${data.seriesName}: ${minutes}分钟`;
}
},
xAxis: {
type: 'category',
data: dates,
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: '时长(分钟)'
},
series: [
{
name: '学习时长',
data: durations,
type: 'line',
smooth: true,
areaStyle: {
color: 'rgba(198, 40, 40, 0.1)'
},
itemStyle: {
color: '#C62828'
}
}
]
};
chartInstance.setOption(option);
}
} catch (error) {
console.error('加载图表数据失败:', error);
}
}
// 加载表格数据
async function loadTableData() {
tableLoading.value = true;
try {
const pageRequest = {
filter: {},
pageParam: {
pageNumber: currentPage.value,
pageSize: pageSize.value
}
};
console.log('📤 请求参数:', pageRequest);
const result = await learningRecordApi.getUserRecordRangePage(pageRequest);
console.log('📥 返回结果:', result);
if (result.success) {
// 优先使用dataList如果没有则使用data.dataList
const dataList = result.dataList || result.data?.dataList || [];
console.log('📊 表格数据:', dataList);
tableData.value = dataList;
totalElements.value = result.data?.pageParam?.totalElements || result.pageParam?.totalElements || 0;
} else {
ElMessage.error(result.message || '加载记录失败');
tableData.value = [];
}
} catch (error) {
console.error('加载表格数据失败:', error);
ElMessage.error('加载记录失败');
tableData.value = [];
} finally {
tableLoading.value = false;
}
}
// 分页大小变化
function handleSizeChange(size: number) {
pageSize.value = size;
loadTableData();
}
// 当前页变化
function handleCurrentChange(page: number) {
currentPage.value = page;
loadTableData();
}
// 格式化学习时长(秒 → 可读格式)
function formatDuration(seconds: number): string {
if (!seconds || seconds === 0) return '0分钟';
if (seconds < 60) {
return `${seconds}`;
}
if (seconds < 3600) {
const minutes = Math.floor(seconds / 60);
return `${minutes}分钟`;
}
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
if (minutes === 0) {
return `${hours}小时`;
}
return `${hours}小时${minutes}分钟`;
} }
function getRecordIcon(type: string) { function getRecordIcon(type: string) {
@@ -170,4 +375,44 @@ function getRecordIcon(type: string) {
color: #999; color: #999;
} }
} }
.empty-state {
text-align: center;
padding: 60px 20px;
color: #999;
font-size: 14px;
p {
margin: 0;
}
}
.chart-container {
margin-bottom: 32px;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
h3 {
font-size: 18px;
font-weight: 600;
color: #141F38;
margin-bottom: 20px;
}
}
.records-table {
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
h3 {
font-size: 18px;
font-weight: 600;
color: #141F38;
margin-bottom: 20px;
}
}
</style> </style>

View File

@@ -238,7 +238,7 @@ onMounted(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.my-achievements { .my-achievements {
// padding: 20px 0; // padding: 20px 0;
height: 100%; // height: 100%;
box-sizing: border-box; box-sizing: border-box;
.achievements-header { .achievements-header {

View File

@@ -58,7 +58,7 @@ const menus = computed(() => {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 20px; gap: 20px;
height: calc(100vh - 76px); min-height: calc(100vh - 76px);
// overflow-y: auto; // overflow-y: auto;
} }