finish Question Page component function

This commit is contained in:
zhangyuheng 2023-12-27 16:32:58 +00:00
parent cd78b47dcb
commit 5d0a4b2834
7 changed files with 247 additions and 163 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>Make a Quick Exame!</title>
</head>
<body>
<div id="app"></div>

1
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "quick-exam-ui",
"version": "0.0.0",
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.3",
"element-plus": "^2.4.4",
"pinia": "^2.1.7",

View File

@ -13,6 +13,7 @@
"format": "prettier --write src/"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.3",
"element-plus": "^2.4.4",
"pinia": "^2.1.7",

View File

@ -1,21 +1,13 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import { RouterLink, RouterView } from 'vue-router';
import HelloWorld from './components/HelloWorld.vue';
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
<RouterView />
</template>

View File

@ -1,87 +1,154 @@
<template>
<!-- Condition Filter From -->
<el-form :inline="true" :model="query_condition" class="demo-form-inline">
<el-form-item label="发布状态">
<el-select v-model="query_condition.is_published" placeholder="请选择">
<el-option label="全部" value="-1" />
<el-option label="已发布" value="1" />
<el-option label="编辑中" value="0" />
</el-select>
</el-form-item>
<el-form-item label="题型">
<el-select v-model="query_condition.types" multiple placeholder="请选择">
<el-option v-for="id in type_ids" :key="id" :label="getQuestionTypeText(id)" :value="id" />
</el-select>
</el-form-item>
<el-form-item label="标签">
<el-select v-model="query_condition.tag_ids" multiple placeholder="请选择">
<el-option v-for="tag in tag_list" :key="tag.id" :label="tag.name" :value="tag.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="关键字">
<el-input v-model="query_condition.search" placeholder="请输入关键字" />
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="timerange_create" type="datarange" range-separator="" value-format="x"
start-placeholder="开始日期" end-placeholder="结束日期" @change="createTimeRangeChange" />
</el-form-item>
<el-form-item label="更新时间">
<el-date-picker v-model="timerange_update" type="datarange" range-separator="" value-format="x"
start-placeholder="开始日期" end-placeholder="结束日期" @change="updateTimeRangeChange" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="fetchQuestionList">搜索</el-button>
</el-form-item>
</el-form>
<!-- Table -->
<el-table :data="page_data.items" :table-layout="'auto'" :default-sort="{ prop: 'id', order: 'ascending' }"
:border="true" :disabled="disabled" @sort-change="handleSortChange">
<el-table-column prop="id" label="ID" :sortable="'custom'" />
<el-table-column prop="title" label="标题" :sortable="'custom'" />
<el-table-column prop="type" label="题型" :sortable="'custom'">
<template #default="scope">
{{ getQuestionTypeText(scope.row.type) }}
</template>
</el-table-column>
<el-table-column label="正确率" :sortable="'custom'">
<template #default="scope">
{{ scope.row.correct_count }} / {{ scope.row.reference_count }}
</template>
</el-table-column>
<el-table-column prop="reference_count" label="引用次数" :sortable="'custom'" />
<el-table-column prop="is_published" label="发布状态" :sortable="'custom'">
<template #default="scope">
<el-tag v-if="scope.row.is_published" type="success">已发布</el-tag>
<el-tag v-else type="info">编辑中</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" :sortable="'custom'">
<template #default="scope">
{{ scope.row.created_at }}
</template>
</el-table-column>
<el-table-column label="更新时间" :sortable="'custom'">
<template #default="scope">
{{ scope.row.updated_at }}
</template>
</el-table-column>
<!-- edit -->
<el-table-column align="right">
<template #header>
操作
</template>
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- Condition Filter From -->
<el-form :inline="true" :model="query_condition" class="demo-form-inline">
<el-form-item label="发布状态">
<el-select v-model="publish_statu" placeholder="请选择" @change="handlePublishChange">
<el-option label="全部" value="-1" />
<el-option label="已发布" value="1" />
<el-option label="编辑中" value="0" />
</el-select>
</el-form-item>
<el-form-item label="题型">
<el-select v-model="types" multiple placeholder="请选择" @change="handleTypeChange">
<el-option label="单选题" value="0" />
<el-option label="多选题" value="1" />
<el-option label="判断题" value="2" />
<el-option label="填空题" value="3" />
<el-option label="简答题" value="4" />
<el-option label="文件上传" value="5" />
</el-select>
</el-form-item>
<el-form-item label="标签">
<el-select v-model="query_condition.tag_ids" multiple placeholder="请选择">
<el-option v-for="tag in tag_list" :key="tag.id" :label="tag.name" :value="tag.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="关键字">
<el-input v-model="query_condition.search" placeholder="请输入关键字" />
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="timerange_create"
type="daterange"
range-separator="至"
value-format="x"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="createTimeRangeChange"
/>
<el-button
:icon="CircleCloseFilled"
@click="
timerange_create = [];
query_condition.after_create_time = 0;
query_condition.before_create_time = 0;
"
type="info"
text
/>
</el-form-item>
<el-form-item label="更新时间">
<el-date-picker
v-model="timerange_update"
type="daterange"
range-separator="至"
value-format="x"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="updateTimeRangeChange"
/>
<el-button
:icon="CircleCloseFilled"
@click="
timerange_update = [];
query_condition.after_update_time = 0;
query_condition.before_update_time = 0;
"
type="info"
text
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="fetchQuestionList">搜索</el-button>
</el-form-item>
</el-form>
<!-- Table -->
<el-table
:data="page_data.items"
:table-layout="'auto'"
:default-sort="{ prop: 'id', order: 'ascending' }"
:border="true"
:disabled="disabled"
@sort-change="handleSortChange"
>
<el-table-column prop="id" label="ID" :sortable="'custom'" />
<el-table-column prop="title" label="标题" :sortable="'custom'" />
<el-table-column prop="type" label="题型" :sortable="'custom'">
<template #default="scope">
{{ getQuestionTypeText(scope.row.type) }}
</template>
</el-table-column>
<el-table-column label="正确率" :sortable="'custom'">
<template #default="scope">
{{ scope.row.correct_count.value / scope.row.reference_count.value }} %
</template>
</el-table-column>
<el-table-column prop="reference_count" label="引用次数" :sortable="'custom'" />
<el-table-column prop="is_published" label="发布状态" :sortable="'custom'">
<template #default="scope">
<el-tag v-if="scope.row.is_published" type="success">已发布</el-tag>
<el-tag v-else type="info">编辑中</el-tag>
</template>
</el-table-column>
<el-table-column label="标签">
<template #default="scope">
<div v-if="scope.row.tags.length === 0"></div>
<div v-else-if="scope.row.tags.length > 3">
<el-tag v-for="tag in scope.row.tags.slice(0, 3)" :key="tag.id" :color="tag.color">{{
tag.name
}}</el-tag>
<el-tag type="info">...</el-tag>
</div>
<div v-else>
<el-tag v-for="tag in scope.row.tags" :key="tag.id" type="info">{{ tag.name }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="创建时间" :sortable="'custom'">
<template #default="scope">
{{ scope.row.created_at }}
</template>
</el-table-column>
<el-table-column label="更新时间" :sortable="'custom'">
<template #default="scope">
{{ scope.row.updated_at }}
</template>
</el-table-column>
<!-- edit -->
<el-table-column align="right">
<template #header> 操作 </template>
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<!-- Pagination -->
<el-pagination v-model:current-page="page_data.page" v-model:page-size="page_data.page_size" :page-sizes="[10, 50, 100]"
:disabled="disabled" :background="true" layout="prev, pager, next, total, jumper, sizes"
:total="page_data.total_count" @current-change="handlePageChange" />
<!-- Pagination -->
<el-pagination
v-model:current-page="page_data.page"
v-model:page-size="page_data.page_size"
:page-sizes="[10, 50, 100]"
:disabled="disabled"
:background="true"
layout="prev, pager, next, total, jumper, sizes"
:total="page_data.total_count"
@current-change="handlePageChange"
@size-change="handlePageSizeChange"
/>
</template>
<script lang="ts" setup>
@ -92,90 +159,109 @@ import { getQuestionTypeText, QuestionType } from '@/interfaces/question';
import type { Tag } from '@/interfaces/tag';
import { isNumber } from 'element-plus/es/utils/types.mjs';
import { getQuestionsByPage } from '@/apis/question';
import { CircleCloseFilled } from '@element-plus/icons-vue';
const timerange_create = ref<number[]>([])
const timerange_update = ref<number[]>([])
const createTimeRangeChange = (val: number[]) => {
query_condition.value.after_create_time = val[0]
query_condition.value.before_create_time = val[1]
}
const updateTimeRangeChange = (val: number[]) => {
query_condition.value.after_update_time = val[0]
query_condition.value.before_update_time = val[1]
}
const timerange_create = ref<number[]>([]);
const timerange_update = ref<number[]>([]);
const createTimeRangeChange = (val: string[]) => {
query_condition.value.after_create_time = Number(val[0]) / 1000;
query_condition.value.before_create_time = Number(val[1]) / 1000;
};
const updateTimeRangeChange = (val: string[]) => {
query_condition.value.after_update_time = Number(val[0]) / 1000;
query_condition.value.before_update_time = Number(val[1]) / 1000;
};
const type_ids = ref<number[]>([])
const disabled = ref<boolean>(false); // before data loaded, disable pagination
const disabled = ref<boolean>(false) // before data loaded, disable pagination
const publish_statu = ref<string>('-1');
const types = ref<string[]>([]);
const page_data = ref<QuestionPage>({
page: 0,
page_size: 0,
total_count: 0,
total_page: 0,
items: []
})
page: 1,
page_size: 10,
total_count: 0,
total_page: 0,
items: [],
});
const tag_list = ref<Tag[]>([])
const tag_list = ref<Tag[]>([]);
const query_condition = ref<QuestionQueryCondition>({
page: 1,
page_size: 10,
sort_by: 'id',
sort_order: ORDER_TYPE.ASC,
search: '',
is_published: -1,
tag_ids: [],
types: [],
after_create_time: 0,
before_create_time: 0,
after_update_time: 0,
before_update_time: 0
})
page: 1,
page_size: 10,
sort_by: 'id',
sort_order: ORDER_TYPE.ASC,
search: '',
is_published: -1,
tag_ids: [],
types: [],
after_create_time: 0,
before_create_time: 0,
after_update_time: 0,
before_update_time: 0,
});
const handlePageChange = (val: number) => {
query_condition.value.page = val
fetchQuestionList()
query_condition.value.page = page_data.value.page
query_condition.value.page_size = page_data.value.page_size
}
query_condition.value.page = val;
fetchQuestionList();
query_condition.value.page = page_data.value.page;
query_condition.value.page_size = page_data.value.page_size;
};
const handlePublishChange = (val: string) => {
query_condition.value.is_published = Number(val);
};
const handleTypeChange = (val: string[]) => {
types.value = val;
query_condition.value.types = val.map((item) => Number(item));
};
const handlePageSizeChange = (val: number) => {
query_condition.value.page_size = val;
fetchQuestionList();
query_condition.value.page = page_data.value.page;
query_condition.value.page_size = page_data.value.page_size;
};
const handleSortChange = (val: any) => {
query_condition.value.sort_by = val.prop
query_condition.value.sort_order = val.order === 'ascending' ? ORDER_TYPE.ASC : ORDER_TYPE.DESC
fetchQuestionList()
}
query_condition.value.sort_by = val.prop;
query_condition.value.sort_order = val.order === 'ascending' ? ORDER_TYPE.ASC : ORDER_TYPE.DESC;
fetchQuestionList();
};
const handleEdit = (index: number, row: any) => {
// todo: go to edit question view
}
// todo: go to edit question view
};
const handleDelete = (index: number, row: any) => {
// todo: pop up a warning
}
// todo: pop up a warning
};
const fetchQuestionList = () => {
disabled.value = true
getQuestionsByPage(query_condition.value).then((res) => {
page_data.value = res
}).catch((err) => {
console.log(err)
disabled.value = true;
getQuestionsByPage(query_condition.value)
.then((res) => {
page_data.value = res;
})
disabled.value = false
}
// on mounted
const mounted = () => {
for (let key in QuestionType) {
if (isNumber(key)) {
type_ids.value.push(Number(key))
}
}
// todo get tag list
fetchQuestionList()
disabled.value = false
}
.catch((err) => {
console.log(err);
});
disabled.value = false;
};
// todo get tag list
fetchQuestionList();
disabled.value = false;
</script>
<style>
.demo-form-inline {
margin-bottom: 20px;
}
.demo-form-inline .el-form-item {
margin-right: 20px;
}
</style>

View File

@ -45,10 +45,10 @@ export interface Question {
correct_count: number;
create_at: string;
update_at: string;
tags: Tag[];
}
export interface QuestionDetail extends Question {
tags: Tag[];
answers: Answer[];
question_contents: QuestionContent[];
sub_questions: QuestionDetail[];

View File

@ -8,8 +8,12 @@ import router from './router';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
const app = createApp(App);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(ElementPlus);
app.use(createPinia());