269 lines
6.3 KiB
Vue
269 lines
6.3 KiB
Vue
<template>
|
|
<v-data-table :loading="loading" :headers="(headers as any)" :items="records"
|
|
:sort-by="[{ key: 'id', order: 'desc' }]" multi-sort items-per-page-text="每页">
|
|
<template v-slot:loading>
|
|
<v-skeleton-loader></v-skeleton-loader>
|
|
</template>
|
|
<template v-slot:top>
|
|
<v-toolbar flat>
|
|
<v-toolbar-title>所有提交记录</v-toolbar-title>
|
|
<v-btn @click="refresh" class="mb-2" color="primary" dark>
|
|
刷新
|
|
</v-btn>
|
|
</v-toolbar>
|
|
</template>
|
|
<template v-slot:item="{ item }">
|
|
<tr>
|
|
<td>{{ item.id }}</td>
|
|
<td>{{ item.user_id }}</td>
|
|
<td>{{ item.problem }}</td>
|
|
<td>
|
|
<v-chip label :color="statusColor(item.status)" variant="flat">
|
|
{{ item.status.toUpperCase() }}
|
|
</v-chip>
|
|
</td>
|
|
<td>{{ decorateAnswer(item.user_id, item.answer) }}</td>
|
|
<td>{{ convertTime(item.timestamp) }}</td>
|
|
</tr>
|
|
</template>
|
|
</v-data-table>
|
|
<v-dialog v-model="dialogShow" width="auto">
|
|
<v-card max-width="400" prepend-icon="mdi-update" :text="dialogText" :title="dialogTitle">
|
|
<template v-slot:actions>
|
|
<v-btn class="ms-auto" text="Ok" @click="dialogShow = false"></v-btn>
|
|
</template>
|
|
</v-card>
|
|
</v-dialog>
|
|
</template>
|
|
<script lang="ts" setup>
|
|
import { useAuthStore } from '@/store/auth';
|
|
import axios, { Axios, AxiosError } from 'axios';
|
|
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
|
import { jwtDecode, type JwtPayload } from 'jwt-decode';
|
|
|
|
const authStore = useAuthStore();
|
|
const storedToken = ref(authStore.token);
|
|
|
|
interface KqmJwt extends JwtPayload {
|
|
user_id: string;
|
|
};
|
|
|
|
const userId = computed(() => {
|
|
if (storedToken.value != '') {
|
|
let data: KqmJwt = jwtDecode(storedToken.value);
|
|
return data.user_id;
|
|
}
|
|
return '';
|
|
});
|
|
|
|
type UserPermissionResponse = { success?: string, permission?: string, error?: string };
|
|
|
|
const queryPermission = async () => {
|
|
try {
|
|
const formData = new FormData;
|
|
formData.append("user_id", userId.value);
|
|
let res = await axios.post('/api/auth/permission', formData);
|
|
return res.data as UserPermissionResponse;
|
|
} catch (e) {
|
|
let ex = e as AxiosError;
|
|
return ex.response?.data as UserPermissionResponse;
|
|
}
|
|
}
|
|
|
|
const userPermission = ref('');
|
|
|
|
const updateUserPermission = async () => {
|
|
let res = await queryPermission();
|
|
if (res?.success) {
|
|
userPermission.value = res.permission as string;
|
|
} else {
|
|
userPermission.value = '';
|
|
}
|
|
}
|
|
|
|
watch(userId, updateUserPermission, { immediate: true });
|
|
|
|
const statusColor = (status: 'ac' | 'wa' | 'uke') => {
|
|
switch (status) {
|
|
case 'ac': return 'green';
|
|
case 'wa': return 'red';
|
|
case 'uke': return 'black';
|
|
}
|
|
}
|
|
|
|
const decorateAnswer = (user_id: string, answer: string) => {
|
|
if (userPermission.value == '1') {
|
|
return answer;
|
|
} else if (userPermission.value == '2' && userId.value == user_id) {
|
|
return answer;
|
|
} else if (!authStore.isAuthenticated) {
|
|
return '登录后查看';
|
|
} else {
|
|
return '权限不足';
|
|
}
|
|
}
|
|
|
|
const convertTime = (timestamp: number) => {
|
|
let date = new Date(timestamp);
|
|
let Y = date.getFullYear();
|
|
let M = date.getMonth() + 1;
|
|
let D = date.getDate();
|
|
let H = date.getHours();
|
|
let m = date.getMinutes();
|
|
let s = date.getSeconds();
|
|
return `${Y}-${M}-${D} ${H}:${m < 10 ? '0' : ''}${m}:${s}`;
|
|
}
|
|
|
|
const loading = ref(false);
|
|
const refresh = async () => {
|
|
loading.value = true;
|
|
await initialize();
|
|
loading.value = false;
|
|
};
|
|
|
|
const dialogShow = ref(false);
|
|
const dialogTitle = ref('');
|
|
const dialogText = ref('');
|
|
|
|
const dialog = (title: string, text: string) => {
|
|
dialogTitle.value = title;
|
|
dialogText.value = text;
|
|
dialogShow.value = true;
|
|
};
|
|
|
|
const headers = ref([
|
|
{
|
|
title: '记录 ID',
|
|
align: 'center',
|
|
key: 'id'
|
|
},
|
|
{
|
|
title: '用户',
|
|
align: 'center',
|
|
key: 'user_id'
|
|
},
|
|
{
|
|
title: '题号',
|
|
key: 'problem',
|
|
align: 'center'
|
|
},
|
|
{
|
|
title: '状态',
|
|
key: 'status',
|
|
align: 'center'
|
|
},
|
|
{
|
|
title: '答案',
|
|
key: 'answer',
|
|
align: 'center'
|
|
},
|
|
{
|
|
title: '时间',
|
|
key: 'timestamp',
|
|
align: 'center'
|
|
},
|
|
]);
|
|
|
|
interface Record {
|
|
id: number,
|
|
user_id: string,
|
|
problem: number,
|
|
status: 'ac' | 'wa' | 'uke',
|
|
answer: string,
|
|
timestamp: number
|
|
};
|
|
|
|
const records = ref<Record[]>([]);
|
|
|
|
const decorate = <T>(ex: AxiosError) => {
|
|
if (ex.response?.data) {
|
|
return ex.response?.data as T;
|
|
} {
|
|
return { error: ex.message } as T;
|
|
}
|
|
}
|
|
|
|
type RecordCountResponse = { success?: string, result?: number, error?: string };
|
|
|
|
const requestRecordCount = async () => {
|
|
try {
|
|
const formData = new FormData;
|
|
formData.append("action", "count");
|
|
let res = await axios.post('/api/study/records', formData);
|
|
return res.data as RecordCountResponse;
|
|
} catch (e) {
|
|
return decorate<RecordCountResponse>(e as AxiosError);
|
|
}
|
|
};
|
|
|
|
type RecordData = {
|
|
problem: number,
|
|
user_id: string,
|
|
data: {
|
|
answer: string,
|
|
status: "ac" | "wa" | "uke",
|
|
timestamp: number
|
|
}
|
|
};
|
|
|
|
type QueryRecordResponse = {
|
|
success?: string,
|
|
error?: string,
|
|
record?: RecordData
|
|
};
|
|
|
|
const requestQueryRecord = async (id: number) => {
|
|
try {
|
|
const formData = new FormData;
|
|
formData.append("action", "query");
|
|
formData.append("id", `${id}`);
|
|
let res = await axios.post('/api/study/records', formData);
|
|
return res.data as QueryRecordResponse;
|
|
} catch (e) {
|
|
return decorate<QueryRecordResponse>(e as AxiosError);
|
|
}
|
|
}
|
|
|
|
|
|
const initialize = async () => {
|
|
let res = await requestRecordCount();
|
|
console.log(res);
|
|
if (res?.error) {
|
|
dialog('发生异常', res.error);
|
|
return;
|
|
}
|
|
let cnt = res.result as number;
|
|
records.value = [];
|
|
for (let id = cnt; id >= 0; --id) {
|
|
let resp = await requestQueryRecord(id);
|
|
if (resp?.success) {
|
|
let recordData = resp.record as RecordData;
|
|
let record: Record = {
|
|
id,
|
|
user_id: recordData.user_id,
|
|
problem: recordData.problem,
|
|
status: recordData.data.status,
|
|
answer: recordData.data.answer,
|
|
timestamp: recordData.data.timestamp
|
|
}
|
|
records.value.push(record);
|
|
}
|
|
}
|
|
records.value.push({
|
|
id: 114514,
|
|
user_id: '114514',
|
|
problem: 1919810,
|
|
status: 'uke',
|
|
answer: 'NaN',
|
|
timestamp: 114514
|
|
});
|
|
};
|
|
|
|
onMounted(initialize);
|
|
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
td {
|
|
text-align: center;
|
|
}
|
|
</style> |