默认不管怎么分页都是1-N,需要加上这个代码就可以变成1-N 10-N
:seq-config="{ startIndex: (tablePage.pageNum - 1) * tablePage.pageSize }"
整体效果
代码
<template>
<div class="main">
<!-- 查询控件的显示 -->
<div class="search" id="searchPannel">
<a-card>
<a-form-model ref="queryForm" layout="inline" :model="queryForm" :label-col="{ span: 5 }" :wrapper-col="{ span: 19 }">
<a-row class="row">
<a-col :span="6">
<!-- 活动 -->
<a-form-model-item :label="this.$t('query.activity')" style="width: 90%" prop="activity_id">
<a-select
v-model="queryForm.activity_id"
:placeholder="this.$t('query.activityPlaceholder')"
allow-clear
>
<a-select-option v-for="item in activityList" :key="item.id" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
<a-col :span="6">
<!-- 题目描述 -->
<a-form-model-item :label="this.$t('query.description')" style="width: 90%" prop="description">
<a-input :placeholder="this.$t('query.descriptionPlaceholder')" allow-clear v-model="queryForm.description" />
</a-form-model-item>
</a-col>
<a-col :span="6">
<!-- 查询 -->
<a-button type="primary" @click="loadData(1,tablePage.pageSize)">
<a-icon type="search" />{{ $t("query.search") }}
</a-button>
<!-- 重置 -->
<a-button @click="searchReset">
<a-icon type="retweet" />{{ $t("query.reset") }}
</a-button>
</a-col>
</a-row>
</a-form-model>
</a-card>
</div>
<!-- 列表控件的显示 -->
<div class="table" :style="'height: calc(100vh - ' + dynGridHeight + 'px - 50px - 40px - 20px - 16px)'">
<vxe-grid border show-overflow size="small" ref="xTable" height="auto" :loading="loading" :row-config="{ isCurrent: true }" :toolbar-config="tableToolbar" :columns="tableColumn" :data="tableData" :keyboard-config="{ isArrow: true }" :column-config="{ resizable: true }" :checkbox-config="{ checkStrictly: true, showHeader: true }" :seq-config="{ startIndex: (tablePage.pageNum - 1) * tablePage.pageSize }" @checkbox-all="checkALL" @checkbox-change="checkChange">
<!-- 表格按钮插槽 -->
<template #left>
<!-- 新增 -->
<a-button type="primary" @click="add(null)">
<a-icon type="plus" />{{ $t("table.add") }}
</a-button>
<!-- 删除 -->
<a-button type="danger" @click="delMultiple()">
<a-icon type="close" />{{ $t("table.del") }}
</a-button>
</template>
<!-- 分页插件 -->
<template #pager>
<vxe-pager :layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']" :current-page.sync="tablePage.pageNum" :page-size.sync="tablePage.pageSize" :total="tablePage.total" @page-change="gridPageChange">
</vxe-pager>
</template>
<template #option_def="{ row }">
<!-- 修改 -->
<a class="primary" @click="edit(row)">{{ $t("table.edit") }}</a>
<span>|</span>
<!-- 删除 -->
<a @click="del(row)" class="primary">{{ $t("table.del") }}</a>
</template>
</vxe-grid>
</div>
<!-- 添加编辑窗口 -->
<drag-modal ref="dragModal" :visible="visible" :bodyStyle="'height:700px;overflow:auto;'" :title="title" @ok="formOK" @cancel="formCancel">
<!-- 数据处理中... -->
<a-spin :tip="this.$t('dataProcessing')" :spinning="formLoading">
<a-form-model ref="form" :model="form" layout="vertical" :rules="rules">
<a-row class="row" :gutter="32">
<a-col :span="24">
<!-- 活动 -->
<a-form-model-item ref="activity_id" :label="this.$t('form.activity')" prop="activity_id">
<a-select
v-model="form.activity_id"
:placeholder="this.$t('form.pleaseSelect')"
allow-clear
>
<a-select-option v-for="item in activityList" :key="item.id" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
</a-row>
<a-row class="row" :gutter="32">
<a-col :span="24">
<!-- 题目描述 -->
<a-form-model-item ref="description" :label="this.$t('form.description')" prop="description">
<a-textarea :placeholder="this.$t('form.pleaseEnter')" v-model="form.description" :rows="4" />
</a-form-model-item>
</a-col>
</a-row>
<a-row class="row" :gutter="32">
<a-col :span="24">
<!-- 题目图片 -->
<a-form-model-item ref="image_url" :label="this.$t('form.imageUrl')" prop="image_url">
<div class="upload-container">
<a-upload
name="file"
:multiple="false"
:action="null"
:before-upload="beforeUpload"
@change="handleChange"
accept="image/*"
list-type="picture-card"
:file-list="fileList"
>
<div v-if="fileList.length < 1">
<a-icon type="plus" />
<div class="ant-upload-text">{{ $t("form.upload") }}</div>
</div>
</a-upload>
</div>
</a-form-model-item>
</a-col>
</a-row>
<a-row class="row" :gutter="32">
<a-col :span="12">
<!-- 选项A -->
<a-form-model-item ref="option_a" :label="this.$t('form.optionA')" prop="option_a">
<a-input :placeholder="this.$t('form.pleaseEnter')" allow-clear v-model="form.option_a" />
</a-form-model-item>
</a-col>
<a-col :span="12">
<!-- 选项B -->
<a-form-model-item ref="option_b" :label="this.$t('form.optionB')" prop="option_b">
<a-input :placeholder="this.$t('form.pleaseEnter')" allow-clear v-model="form.option_b" />
</a-form-model-item>
</a-col>
</a-row>
<a-row class="row" :gutter="32">
<a-col :span="12">
<!-- 选项C -->
<a-form-model-item ref="option_c" :label="this.$t('form.optionC')" prop="option_c">
<a-input :placeholder="this.$t('form.pleaseEnter')" allow-clear v-model="form.option_c" />
</a-form-model-item>
</a-col>
<a-col :span="12">
<!-- 选项D -->
<a-form-model-item ref="option_d" :label="this.$t('form.optionD')" prop="option_d">
<a-input :placeholder="this.$t('form.pleaseEnter')" allow-clear v-model="form.option_d" />
</a-form-model-item>
</a-col>
</a-row>
<a-row class="row" :gutter="32">
<a-col :span="12">
<!-- 正确答案 -->
<a-form-model-item ref="correct_answer" :label="this.$t('form.correctAnswer')" prop="correct_answer">
<a-select style="width: 100%" v-model="form.correct_answer" :placeholder="this.$t('form.pleaseSelect')">
<a-select-option value="A">A</a-select-option>
<a-select-option value="B">B</a-select-option>
<a-select-option value="C">C</a-select-option>
<a-select-option value="D">D</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
</a-row>
<a-row class="row" :gutter="32">
<a-col :span="24">
<!-- 答案解析 -->
<a-form-model-item ref="answer_analysis" :label="this.$t('form.answerAnalysis')" prop="answer_analysis">
<a-textarea :placeholder="this.$t('form.pleaseEnter')" v-model="form.answer_analysis" :rows="4" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
</a-spin>
</drag-modal>
</div>
</template>
<script>
import {
listQuizQuestion,
getQuizQuestion,
addQuizQuestion,
updateQuizQuestion,
delQuizQuestion,
} from "@/api/biz/quizQuestion";
import { listQuizActivity } from "@/api/biz/quizActivity";
//弹窗可拖动插件
import dragModal from "@/components/dialog/dragModal";
//处理中
const key = "loading";
export default {
name: "quizQuestionIndex",
i18n: require("./i18n"),
components: { dragModal },
data() {
return {
//查询区域Start-------------------------------------------
//是否加载中
loading: false,
//查询控件的显示
searchShow: false,
//活动列表
activityList: [],
//Grid的工具栏
tableToolbar: {
// 表格默认样式
perfect: true,
zoom: true, //全屏
custom: true, //自定义显示列
slots: {
buttons: "left", // 自定义左边按钮
},
},
//grid的动态高度
dynGridHeight: 50,
//查询条件
queryForm: {
activity_id: undefined,
quiz_id: undefined,
description: "",
},
//查询区域End-------------------------------------------
//列表区域Start-------------------------------------------
tablePage: {
total: 0,
pageNum: 1,
pageSize: 10,
orderBy: "create_time",
sort: "desc"
},
//grid的数据
tableData: [],
//grid的显示列
tableColumn: [
{ type: "checkbox", width: 50, align: "center" },
{ type: "seq", width: 50, align: "center" },
{ field: "description", title: this.$t("table.description") }, // 题目描述
{ field: "image_url", width: 120, title: this.$t("table.imageUrl") }, // 图片URL
{ field: "option_a", width: 120, title: this.$t("table.optionA") }, // 选项A
{ field: "option_b", width: 120, title: this.$t("table.optionB") }, // 选项B
{ field: "option_c", width: 120, title: this.$t("table.optionC") }, // 选项C
{ field: "option_d", width: 120, title: this.$t("table.optionD") }, // 选项D
{ field: "correct_answer", width: 100, title: this.$t("table.correctAnswer") }, // 正确答案
{ field: "answer_analysis", title: this.$t("table.answerAnalysis") }, // 答案解析
{ field: "create_time", width: 160, title: this.$t("table.createTime") }, // 创建时间
{ field: "update_time", width: 160, title: this.$t("table.updateTime") }, // 更新时间
{ field: "option", width: 120, title: this.$t("table.option"), slots: { default: "option_def" } }, // 操作
],
//列表区域End-------------------------------------------
//表单区域Start-------------------------------------------
//是否显示表单
visible: false,
//表单标题
title: "",
//表单是否加载中
formLoading: false,
//表单数据
form: {
id: undefined,
activity_id: undefined,
description: "",
image_url: "",
option_a: "",
option_b: "",
option_c: "",
option_d: "",
correct_answer: undefined,
answer_analysis: "",
imageFile: undefined
},
//表单校验规则
rules: {
activity_id: [{ required: true, message: this.$t("rules.required"), trigger: "change" }],
//description: [{ required: true, message: this.$t("rules.required"), trigger: "blur" }],
option_a: [{ required: true, message: this.$t("rules.required"), trigger: "blur" }],
option_b: [{ required: true, message: this.$t("rules.required"), trigger: "blur" }],
option_c: [{ required: true, message: this.$t("rules.required"), trigger: "blur" }],
option_d: [{ required: true, message: this.$t("rules.required"), trigger: "blur" }],
correct_answer: [{ required: true, message: this.$t("rules.required"), trigger: "change" }],
},
//API基础路径
BASE_API: process.env.BASE_API,
//表单区域End-------------------------------------------
fileList: [], // 文件列表
};
},
created() {
this.loadQuizList();
this.loadData();
},
methods: {
//加载活动列表
loadQuizList() {
listQuizActivity().then(res => {
this.activityList = res.data;
});
},
//查询区域Start-------------------------------------------
//加载数据
loadData(pageNum, pageSize) {
this.loading = true;
listQuizQuestion(Object.assign(this.queryForm, this.tablePage))
.then((res) => {
this.tableData = res.data;
this.tablePage.total = res.total;
this.loading = false;
})
.catch(() => {
this.loading = false;
});
},
//重置查询
searchReset() {
this.queryForm = {
quiz_id: undefined,
description: "",
};
this.loadData(1, this.tablePage.pageSize);
},
//查询区域End-------------------------------------------
//列表区域Start-------------------------------------------
//表格全选
checkALL({ records }) {
this.selectedRows = records;
},
//表格选择
checkChange({ records }) {
this.selectedRows = records;
},
//分页改变
gridPageChange({ currentPage, pageSize }) {
this.tablePage.pageNum = currentPage;
this.tablePage.pageSize = pageSize;
this.loadData(currentPage, pageSize);
},
//列表区域End-------------------------------------------
//表单区域Start-------------------------------------------
//新增
add(row) {
this.title = this.$t("form.add");
this.form = {
id: undefined,
activity_id: undefined,
description: "",
image_url: "",
option_a: "",
option_b: "",
option_c: "",
option_d: "",
correct_answer: undefined,
answer_analysis: "",
imageFile: undefined
};
this.fileList = []; // 清空文件列表
this.visible = true;
},
//编辑
edit(row) {
this.title = this.$t("form.edit");
this.formLoading = true;
// 先清空表单和图片
this.form = {
id: undefined,
activity_id: undefined,
description: "",
image_url: "",
option_a: "",
option_b: "",
option_c: "",
option_d: "",
correct_answer: undefined,
answer_analysis: "",
imageFile: undefined
};
this.fileList = [];
getQuizQuestion(row.id)
.then((res) => {
this.form = res.data;
// 如果有图片,添加到文件列表
if (this.form.image_url) {
this.fileList = [{
uid: '-1',
name: 'image.png',
status: 'done',
url: this.form.image_url.startsWith('http') ? this.form.image_url : this.BASE_API + this.form.image_url
}];
}
this.visible = true;
this.formLoading = false;
})
.catch(() => {
this.formLoading = false;
});
},
//删除
del(row) {
this.$confirm({
title: this.$t("confirm.title"),
content: this.$t("confirm.content"),
okText: this.$t("confirm.okText"),
okType: "danger",
cancelText: this.$t("confirm.cancelText"),
onOk: () => {
delQuizQuestion([row.id])
.then(() => {
this.$message.success(this.$t("message.deleteSuccess"));
this.loadData();
})
.catch(() => {});
},
});
},
//批量删除
delMultiple() {
if (this.selectedRows.length === 0) {
this.$message.warning(this.$t("message.pleaseSelect"));
return;
}
this.$confirm({
title: this.$t("confirm.title"),
content: this.$t("confirm.content"),
okText: this.$t("confirm.okText"),
okType: "danger",
cancelText: this.$t("confirm.cancelText"),
onOk: () => {
const ids = this.selectedRows.map((item) => item.id);
delQuizQuestion(ids)
.then(() => {
this.$message.success(this.$t("message.deleteSuccess"));
this.loadData();
})
.catch(() => {});
},
});
},
//表单确认
formOK() {
this.$refs["form"].validate(valid => {
if (valid) {
this.formLoading = true; // 开始加载
// 创建FormData对象
const formData = new FormData();
// 添加文件
if (this.form.imageFile) {
formData.append('file', this.form.imageFile);
}
// 添加表单数据,移除imageFile字段
const formDataToSend = { ...this.form };
delete formDataToSend.imageFile;
formData.append('data', JSON.stringify(formDataToSend));
// 如果没有文件且fileList为空,说明用户删除了图片
if (!this.form.imageFile && this.fileList.length === 0 && this.form.id) {
formData.append('clearImage', 'true');
}
if (this.form.id != undefined) {
updateQuizQuestion(formData).then(response => {
this.$message.success(this.$t("message.updateSuccess"));
this.visible = false;
this.loadData();
}).finally(() => {
this.formLoading = false; // 结束加载
});
} else {
addQuizQuestion(formData).then(response => {
this.$message.success(this.$t("message.addSuccess"));
this.visible = false;
this.loadData();
}).finally(() => {
this.formLoading = false; // 结束加载
});
}
}
});
},
//表单取消
formCancel() {
this.visible = false;
this.fileList = [];
},
//上传前校验
beforeUpload(file) {
const isImage = file.type.startsWith('image/');
if (!isImage) {
this.$message.error(this.$t('message.uploadImageOnly'));
return false;
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.$message.error(this.$t('message.imageSizeLimit'));
return false;
}
// 保存文件对象到表单数据中
this.form.imageFile = file;
return false; // 阻止自动上传
},
//上传状态改变
handleChange(info) {
if (info.file.status === 'uploading') {
return;
}
if (info.file.status === 'done') {
this.$message.success(this.$t('message.selectSuccess'));
} else if (info.file.status === 'error') {
this.$message.error(this.$t('message.selectFailed'));
}
// 更新文件列表
this.fileList = info.fileList.slice(-1); // 只保留最后一个文件
},
//表单区域End-------------------------------------------
},
};
</script>
<style lang="less" scoped>
@import "../../assets-common/common.less";
.upload-container {
display: flex;
align-items: flex-start;
gap: 16px;
.image-preview {
max-width: 200px;
max-height: 200px;
border: 1px solid #d9d9d9;
border-radius: 4px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
</style>