前后端对接完成

FRONT_a
helloworld180 1 month ago
parent d298b5856b
commit a377375030

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<link rel="icon" href="/点名.svg">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>点点小助手</title>
</head>
<body>
<div id="app"></div>

@ -35,31 +35,46 @@
}
.lucky-icon {
width: 780px;
width: 700px;
position: relative;
}
.stage-icon {
position: absolute;
width: 900px;
top: 44%;
}
.student-info {
position: absolute;
text-align: center; /* 确保文本居中 */
top: 34%;
}
.student-id {
.student-id, .student-name {
font-size: 75px;
font-weight: bold;
color: black;
}
/* .student-id {
position: absolute;
top: 35%;
top: 33%;
font-size: 75px;
font-weight: bold;
color: black;
}
.student-name {
position: absolute;
top: 50%;
top: 45%;
font-size: 75px;
font-weight: bold;
color: black;
}
} */
.student-msg {
position: absolute;
left: 5%;
right: 5%;
top: 65%;
font-size: 35px;
top: 62.5%;
font-size: 33px;
text-align: center;
font-weight: bold;
color: #333;

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728590895946" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6825" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M0 192v640c0 70.7 57.3 128 128 128h352c17.7 0 32-14.3 32-32s-14.3-32-32-32H128c-35.3 0-64-28.7-64-64V192c0-35.3 28.7-64 64-64h352c17.7 0 32-14.3 32-32s-14.3-32-32-32H128C57.3 64 0 121.3 0 192z" p-id="6826" fill="#9276cf"></path><path d="M1013.3 535.7L650.9 863.3c-41.1 37.2-106.9 8-106.9-47.5V685c0-4.4-3.6-8-8-8H224c-17.7 0-32-14.3-32-32V379c0-17.7 14.3-32 32-32h312c4.4 0 8-3.6 8-8V208.1c0-55.5 65.8-84.7 106.9-47.5l362.4 327.6c14.1 12.8 14.1 34.8 0 47.5z" p-id="6827" fill="#9276cf"></path></svg>

After

Width:  |  Height:  |  Size: 830 B

@ -0,0 +1,10 @@
<svg width="1061" height="192" viewBox="0 0 1061 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_238_16)">
<path d="M901.075 0L895.903 2.00872L1024.8 63.9442L823.499 165.887H245.856L40.5782 62.1029L165.495 2.00872L160.324 0L0.795654 76.6661L222.782 191.833H839.014L1061 76.6661L901.075 0ZM241.878 168.063L243.071 168.565H827.079L1029.57 65.9529L1038.32 70.1377L829.864 178.943H235.91L25.4608 69.3008L35.8043 64.279L241.878 168.063ZM835.433 188.987H225.965L9.94564 76.8335L21.0848 71.4769L231.534 181.454L232.728 181.956H833.046L1042.7 72.4813L1051.85 76.8335L835.433 188.987Z" fill="#F8B642"/>
</g>
<defs>
<clipPath id="clip0_238_16">
<rect width="1061" height="192" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 739 B

@ -4,7 +4,7 @@ import {getToken} from '@/token/auth' // 注意这里使用了解构赋值来导
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:8080/api', // 配置基础URL
baseURL: ' http://p4ue3i.natappfree.cc/api', // 配置基础URL
timeout: 5000, // 请求超时时间
});
@ -13,9 +13,13 @@ service.interceptors.request.use(
config => {
// 在发送请求之前做些什么
const token = getToken(); // 获取token的方式取决于你的应用
console.log('请求拦截器的token是' +token)
console.log('Request Config:', config);
if (token) {
config.headers['Authorization'] = `Bearer ${token}`; // 设置token
// axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
}
config.headers['Accept'] = 'application/json';
return config;
},
error => {
@ -29,62 +33,32 @@ service.interceptors.request.use(
service.interceptors.response.use(
response => {
// 对响应数据做点什么
// 注意这里返回已经包含data
// 注意这里返回已经包含data
const res = response.data;
// 你可以根据实际情况在这里添加一些通用的响应处理逻辑
// 例如,根据返回的状态码判断请求是否成功
if (res.code !== 200) {
// if (res.code !== 200) {
// 业务错误处理,比如弹窗提示等
return Promise.reject(new Error(res.message || 'Error'));
} else {
// return Promise.reject(new Error(res.message || 'Error'));
// } else {
return res;
}
// }
},
error => {
// 对响应错误做点什么
if (error.response) {
// if (error.response) {
// 请求已发出但服务器响应的状态码不在2xx的范围
console.error('Error status:', error.response.status);
console.error('Error data:', error.response.data);
} else if (error.request) {
// console.error('Error status:', error.response.status);
// console.error('Error data:', error.response.data);
// } else if (error.request) {
// 请求已发出,但没有收到响应
console.error('Error request:', error.request);
} else {
// console.error('Error request:', error.request);
// } else {
// 在设置请求时触发错误
console.error('Error message:', error.message);
}
console.error('响应拦截器errorMessage:', error.message);
// }
return Promise.reject(error);
}
);
export default service;
// 使用
// 导入封装好的axios实例
// import axios from './axiosConfig';
// 登录方法
// methods: {
// async login() {
// try {
// const response = await axios.post('/api/login', {
// username: this.username,
// password: this.password
// });
// // 假设token在响应的data字段中
// const token = response.data.token;
// // 存储token
// setToken(token);
// // 登录成功后的操作,比如跳转到主页
// this.$router.push('/home');
// } catch (error) {
// // 处理登录错误
// console.error('Login Error:', error);
// }
// }
// }

@ -20,10 +20,12 @@
</template>
<script>
import axios from 'axios';
import axios from '@/utils/axiosConfig';
import { ref } from 'vue';
export default {
data() {
// const names = ref([])
return {
//
maxTexts: 80,
@ -32,27 +34,45 @@
};
},
mounted() {
// this.fetchNames();
this.names = ['文字1', '文字2', '文字3', '文字4', '文字5'];
this.fetchNames();
// this.names = ['1', '2', '3', '4', '5'];
this.startRain();
},
methods: {
// names
async fetchNames() {
try {
const response = await axios.get('/students/export-students');
this.names = response.data;
const response = await axios.get('/students/names',null);
// this.names = response.data;
// console.log('' + response)
if (Array.isArray(response)) {
this.names = response;
console.log('后端传值response', this.names);
} else if (typeof response === 'object' && Array.isArray(response.data)) {
this.names = response.data;
}else{
console.error('后端返回的数据不是数组格式');
this.names = ['文字1', '文字2', '文字3', '文字4', '文字5']; //
console.log('实际接收到的数据:', response.data);
}
if (this.names.length === 0 || this.names.every(name => name.trim() === '')) {
alert('后端传回的字符串数组全为空');
//
// this.names = ['1', '2']; //
return; //
}
} catch (error) {
console.error('获取names失败:', error);
//
this.names = ['令狐', '令狐新锐', '令狐冲', '文字4', '文字5'];
// this.names = ['', '', '', '4', '5'];
}
},
//
getTextStyle() {
const left = Math.floor(Math.random() * 80) + 5; // 10%90%
const size = Math.random() * 1.7 + 0.5; // 0.5em1.5em
const size = Math.random() * 1.8 + 0.5; // 0.5em1.5em
const duration = Math.random() * 2 + 5; // 2-7
return {
left: `${left}%`,

@ -19,13 +19,22 @@
<div @click="gotoChart" class="selectItem">
查看排行
</div>
<div class="back" @click="goLogin">
<div>退出登录</div>
<img src="../assets/image/back.svg" alt="" style="width: 40px;margin-left: 15px;">
</div>
</div>
</div>
</div>
</template>
<script>
import { RouterLink, RouterView } from 'vue-router';
import axios from '@/utils/axiosConfig';
export default {
data() {
return {
@ -44,6 +53,17 @@ export default {
},
gotoCall() {
this.$router.push('/beginCall')
},
async goLogin() {
try {
const response = await axios.post('/teacher/logout');
console.log('退出登录成功', response)
// 退
this.$router.push('/');
} catch (error) {
//
console.error('退出登录错误:', error);
}
}
}
}
@ -83,4 +103,19 @@ export default {
.selectItem:hover {
background-color: #6231F5;
}
.back {
display: flex;
position: absolute;
font-size: 28px;
font-weight: bold;
color: #9276CF;
right: 2.5%;
bottom: 4%;
cursor: pointer;
}
.back:hover {
color: #6231F5;
}
</style>

@ -15,6 +15,7 @@
accept=".xlsx, .xls"
:on-exceed="handleExceed"
:http-request="customUpload"
:auto-upload="false"
>
<div style="font-size: 30px; color: #7D7878;">选择文件</div>
<input placeholder="请选择上传的文件" class="inputFile" />
@ -68,7 +69,7 @@ import { ref } from 'vue'
import { genFileId, ElMessage } from 'element-plus'
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
import router from '@/router';
import axios from 'axios';
import axios from '@/utils/axiosConfig';
const uploadRef = ref<UploadInstance>()
//
@ -84,8 +85,9 @@ const customUpload = (options) => {
const { file } = options;
const formData = new FormData();
formData.append('file', file);
console.log('formData是' + formData)
axios.post('http://localhost:8080/api/students/import', formData, {
axios.post('http://p4ue3i.natappfree.cc/api/students/import', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
@ -95,6 +97,7 @@ const customUpload = (options) => {
type: 'success',
message: '文件导入成功!'
});
close()
console.log('导入结果:', response.data);
})
.catch(error => {
@ -115,7 +118,7 @@ const submitUpload = () => {
async function getTemplateDownloadLink(): Promise<string> {
try {
const response = await axios.get('/students/download-template');
return response.data; //
return response; //
} catch (error) {
console.error('获取下载链接失败:', error);
throw error; //

@ -50,7 +50,7 @@
>
</div>
<div class="forgot-password">
<a href="#">忘记密码?</a>
<!-- <a href="#">忘记密码?</a> -->
</div>
<button type="submit" class="submit-btn"> </button>
</form>
@ -115,7 +115,7 @@
<script>
import axios from '@/utils/axiosConfig';
import {setToken} from '@/token/auth'
import {getToken, setToken} from '@/token/auth'
export default {
data() {
return {
@ -143,22 +143,27 @@
//
async handleLogin() {
try {
const response = await axios.post('/teacher/login', {
username: this.loginForm.phone,
password: this.loginForm.password
const response = await axios.post('/teacher/login', null, {
params: {
username: this.loginForm.phone,
password: this.loginForm.password
}
});
console.log('登录成功', response)
// tokendata
// !!!
const token = response.data.token;
const token = response.token;
// token
setToken(token);
console.log( 'token是' + getToken() )
//
this.$router.push('/home');
} catch (error) {
//
console.error('登录 Error:', error);
console.error('登录错误:', error);
}
},
//
@ -169,9 +174,11 @@
}
try {
//
const response = await axios.post('/teacher/register', {
username: this.registerForm.phone,
password: this.registerForm.password,
const response = await axios.post('/teacher/register', null, {
params:{
username: this.registerForm.phone,
password: this.registerForm.password,
}
});
//
console.log('注册成功', response);

@ -8,9 +8,13 @@
<div class="lucky-info">
<img src="../assets/image/group.svg" alt="" class="lucky-icon">
<div class="student-id">{{ studentId }}</div>
<div class="student-name">{{ studentName }}</div>
<div class="student-info">
<div class="student-id">{{ studentId }}</div>
<div class="student-name">{{ studentName }}</div>
</div>
<img src="../assets/image/stage.svg" alt="" class="stage-icon">
<div class="student-msg" v-if="nameOrQuestion === '提问'">{{ message }}</div>
</div>
<!-- 选择分数 -->
@ -54,13 +58,18 @@
<script>
import axios from '@/utils/axiosConfig';
import router from '@/router';
// import { da } from 'element-plus/es/locale';
export default {
data() {
return {
studentId: '102201338',
studentName: '令狐新锐',
message: '赌徒事件--学生可以在回答问题前下注一定数量的积分,如果回答正确,则按赌注倍数获得积分;如果错误,则失去赌注积分',
// studentId: '102201338',
// studentName: '',
// message: '--',
studentId:'',
studentName:'',
message:'',
selectedScore: null,
customScore: '',
@ -100,13 +109,26 @@
//
try {
// const response = await axios.post('/rollcall/start', null, {
// params: {
// rollCallMode: this.nameOrQuestion,
// triggerRandomEvent: this.triggerRandomEvent,
// wheelOfFortune: this.enableFateWheel,
// }
// });
const response = await axios.post('/rollcall/start', data);
console.log('后端响应:', response.data);
console.log('后端响应:', response);
this.studentId = response.studentId;
this.studentName = response.name;
this.message = response.message;
} catch (error) {
console.error('发送请求时出错:', error);
console.error('发送请求时出错:', error);
if (error.response) {
console.error('错误状态码:', error.response.status);
console.error('错误数据:', error.response.data);
}
}
@ -137,9 +159,10 @@
alert('请选择或输入有效的调整分数');
return;
}
console.log('调整的积分是:' + pointsDelta)
try {
// const response = await axios.post('/students/${this.studentId}/adjustPoints', pointsDelta)
await axios.put(`http://localhost:8080/api/students/${this.studentId}/adjustPoints`, {
await axios.put(`/students/${this.studentId}/adjustPoints`, {
pointsDelta: pointsDelta
});
alert('积分调整成功');
@ -150,7 +173,7 @@
}
},
close() {
this.$router.push('/home')
this.$router.push('/beginCall')
}
}
};

@ -54,7 +54,7 @@
import router from '@/router';
import { ref } from 'vue'
import { ElTooltip } from 'element-plus'
import axios from 'axios';
import axios from '@/utils/axiosConfig';
const form = ref({
nameOrQuestion: '点名',

@ -1,172 +1,147 @@
<template>
<div class="container">
<!-- 头部 -->
<div class="header">
<div>查看排行</div>
<img src="../assets/image/close.png" alt="" class="icon" @click="close">
</div>
<div style="border: 1.5px solid #C8C1C1;"></div>
<!-- 搜索框 -->
<div class="search-bar">
<input
v-model="searchInput"
type="text"
class="search-input"
placeholder="输入需要查询积分同学的学号"
/>
<button class="search-btn" @click="searchStudent"></button>
</div>
<!-- 表格滚动容器 -->
<div class="table-scroll-container">
<!-- 排行榜 -->
<table>
<thead>
<tr>
<th>名次</th>
<th>学号</th>
<th>姓名</th>
<th>积分</th>
</tr>
</thead>
<tbody>
<tr v-for="(student, index) in filteredStudents" :key="student.studentNumber">
<td v-if="student.id === 1"><span class="medal">🥇</span></td>
<td v-else-if="student.id === 2"><span class="medal">🥈</span></td>
<td v-else-if="student.id === 3"><span class="medal">🥉</span></td>
<td v-else class="center-align">{{ student.id }}</td>
<td>{{ student.studentNumber }}</td>
<td>{{ student.name }}</td>
<td>{{ student.points }}</td>
</tr>
</tbody>
</table>
<!-- 分页 -->
<div class="pagination">
{{ totalPages }}当前是第{{ currentPage }}
<button class="page-btn" @click="prevPage" :disabled="currentPage === 1">上一页</button>
<button class="page-btn" @click="nextPage" :disabled="currentPage === totalPages">下一页</button>
</div>
<div class="container">
<!-- 头部 -->
<div class="header">
<div>查看排行</div>
<img src="../assets/image/close.png" alt="" class="icon" @click="close">
</div>
<div style="border: 1.5px solid #C8C1C1;"></div>
<!-- 搜索框 -->
<div class="search-bar">
<input
v-model="searchInput"
type="text"
class="search-input"
placeholder="输入需要查询积分同学的学号"
/>
<button class="search-btn" @click="searchStudent"></button>
</div>
<!-- 表格滚动容器 -->
<div class="table-scroll-container">
<!-- 排行榜 -->
<table>
<thead>
<tr>
<th>名次</th>
<th>学号</th>
<th>姓名</th>
<th>积分</th>
</tr>
</thead>
<tbody>
<tr v-for="(student, index) in paginatedStudents" :key="student.studentNumber">
<td v-if="currentPage === 1 && index === 0"><span class="medal">🥇</span></td>
<td v-else-if="currentPage === 1 && index === 1"><span class="medal">🥈</span></td>
<td v-else-if="currentPage === 1 && index === 2"><span class="medal">🥉</span></td>
<td v-else class="center-align">{{ (currentPage - 1) * pageSize + (index + 1) }}</td>
<td>{{ student.studentNumber }}</td>
<td>{{ student.name }}</td>
<td>{{ student.points }}</td>
</tr>
</tbody>
</table>
<!-- 分页 -->
<div class="pagination">
{{ totalPages }}当前是第{{ currentPage }}
<button class="page-btn" @click="prevPage" :disabled="currentPage === 1">上一页</button>
<button class="page-btn" @click="nextPage" :disabled="currentPage === totalPages">下一页</button>
</div>
</div>
<!-- 底部 -->
<div class="bottom">
<div style="display: flex;">
<div class="confirm" @click="exportData"></div>
<div class="cancel" @click="close"></div>
</div>
<!-- 底部 -->
<div class="bottom">
<div style="display: flex;">
<div class="confirm" @click="exportData"></div>
<div class="cancel" @click="close"></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import router from '@/router';
import axios from '@/utils/axiosConfig';
//
// const students = ref([
// { id: 1, studentNumber: '102201403', name: '', points: 100 },
// { id: 2, studentNumber: '102201338', name: '', points: 99 },
// { id: 3, studentNumber: '102201111', name: '', points: 95 },
// { id: 4, studentNumber: '102201112', name: '', points: 90 },
// { id: 5, studentNumber: '102201113', name: '', points: 89 },
// { id: 6, studentNumber: '102201113', name: '', points: 89 },
// { id: 7, studentNumber: '102201113', name: '', points: 89 },
// { id: 8, studentNumber: '102201113', name: '', points: 89 },
// ])
const students = ref([])
//
// ref
const searchInput = ref('')
const currentPage = ref(1)
const itemsPerPage = ref(6)
const totalPages = ref(Math.ceil(students.value.length / itemsPerPage.value))
//
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++
}
<script setup>
import { ref, computed, onMounted } from 'vue';
import router from '@/router';
import axios from '@/utils/axiosConfig';
const allStudents = ref([]);
const currentPage = ref(1);
const pageSize = 6;
const searchInput = ref('');
const fetchStudents = async () => {
try {
const response = await axios.get('/students/ranking');
allStudents.value = response;
} catch (error) {
console.error('获取学生数据失败:', error);
}
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--
}
};
const filteredStudents = computed(() => {
if (!searchInput.value) return allStudents.value;
return allStudents.value.filter(student =>
student.studentNumber.includes(searchInput.value)
);
});
const totalPages = computed(() => {
return Math.ceil(filteredStudents.value.length / pageSize);
});
const paginatedStudents = computed(() => {
const start = (currentPage.value - 1) * pageSize;
const end = start + pageSize;
return filteredStudents.value.slice(start, end);
});
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
};
//
const searchStudent = () => {
currentPage.value = 1 //
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
//
const filteredStudents = computed(() => {
let filtered = students.value.filter(student =>
student.studentNumber.includes(searchInput.value)
)
let start = (currentPage.value - 1) * itemsPerPage.value
let end = start + itemsPerPage.value
return filtered.slice(start, end)
})
//
const fetchidData = async () => {
try {
const response = await axios.get('/students/id') // API
students.value = response.data // students
} catch (error) {
console.error('Failed to fetch id data:', error)
//
}
};
const searchStudent = () => {
currentPage.value = 1;
};
//
const exportData = async () => {
try {
const response = await axios.get('/students/export-students', {
responseType: 'blob',
});
const url = window.URL.createObjectURL(new Blob([response]));
const a = document.createElement('a');
// a.style.display = 'none';
a.href = url;
a.download = 'students.xlsx';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('导出数据失败:', error);
}
};
//
onMounted(() => {
fetchidData()
})
// Blob
const exportData = async () => {
try {
const response = await axios.get('/students/export-students', {
responseType: 'blob', // blob
});
// URL Blob
const url = window.URL.createObjectURL(new Blob([response.data]));
function close() {
router.push('/home');
}
// a
const a = document.createElement('a');
a.href = url;
a.download = 'filename'; //
document.body.appendChild(a);
a.click();
// URL
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('Failed to export data:', error);
//
}
};
//
function close() {
router.push('/home')
}
</script>
onMounted(() => {
fetchStudents();
});
</script>
<style scoped>
@import '../assets/css/seeChart.css'
</style>
<style scoped>
@import '../assets/css/seeChart.css';
</style>
Loading…
Cancel
Save