<script lang="ts" setup>
|
import type { QuestionBankOption, QuestionEntity } from '#/views/x/tms/question/types';
|
|
import { computed, reactive, ref } from 'vue';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { BasicModal, useModalInner } from '@jnpf/ui/modal';
|
|
import { getQuestionBanks, getQuestionList } from '#/api/x/tms/question';
|
import { labelOfType } from '#/views/x/tms/question/constants';
|
|
defineOptions({ name: 'TmsPaperQuestionPicker' });
|
|
const emit = defineEmits<{
|
confirm: [QuestionEntity[]];
|
}>();
|
|
const { createMessage } = useMessage();
|
|
const banks = ref<QuestionBankOption[]>([]);
|
const loading = ref(false);
|
const list = ref<QuestionEntity[]>([]);
|
const selectedKeys = ref<string[]>([]);
|
const selectedRows = ref<QuestionEntity[]>([]);
|
const excludeIds = ref<Set<string>>(new Set());
|
/** 章节已定题型:选题时锁定,只需再选题库 */
|
const lockedType = ref<string | undefined>();
|
|
const query = reactive({
|
bankId: undefined as string | undefined,
|
keyword: '',
|
currentPage: 1,
|
pageSize: 10,
|
total: 0,
|
});
|
|
const columns = [
|
{ title: '编号', dataIndex: 'questionNo', width: 90 },
|
{ title: '题库', dataIndex: 'bankName', width: 140, ellipsis: true },
|
{ title: '题型', dataIndex: 'questionType', width: 80, key: 'questionType' },
|
{ title: '题干', dataIndex: 'stem', ellipsis: true, key: 'stem' },
|
];
|
|
const typeLabel = computed(() => (lockedType.value ? labelOfType(lockedType.value) : ''));
|
|
const [registerModal, { closeModal }] = useModalInner(async (data: any) => {
|
excludeIds.value = new Set(Array.isArray(data?.excludeIds) ? data.excludeIds : []);
|
lockedType.value = data?.questionType || undefined;
|
query.bankId = undefined;
|
query.keyword = '';
|
query.currentPage = 1;
|
query.total = 0;
|
list.value = [];
|
selectedKeys.value = [];
|
selectedRows.value = [];
|
if (!banks.value.length) {
|
banks.value = (await getQuestionBanks()) || [];
|
}
|
});
|
|
async function loadList() {
|
if (!query.bankId) {
|
list.value = [];
|
query.total = 0;
|
return;
|
}
|
loading.value = true;
|
try {
|
const page = await getQuestionList({
|
bankId: query.bankId,
|
questionType: lockedType.value || '',
|
bizStatus: 'open',
|
keyword: query.keyword || undefined,
|
currentPage: query.currentPage,
|
pageSize: query.pageSize,
|
});
|
list.value = Array.isArray(page?.list) ? page.list : [];
|
query.total = Number(page?.pagination?.total || 0);
|
} catch (e: any) {
|
createMessage.error(e?.message || '加载试题失败');
|
list.value = [];
|
} finally {
|
loading.value = false;
|
}
|
}
|
|
function onSelectChange(keys: (string | number)[], rows: QuestionEntity[]) {
|
selectedKeys.value = keys.map(String);
|
selectedRows.value = rows;
|
}
|
|
function rowDisabled(record: QuestionEntity) {
|
return !!(record.id && excludeIds.value.has(record.id));
|
}
|
|
function stripHtml(html?: string) {
|
if (!html) return '';
|
const text = html.replace(/<[^>]+>/g, '').replace(/ /g, ' ').trim();
|
return text.length > 60 ? `${text.slice(0, 60)}…` : text;
|
}
|
|
function onBankChange() {
|
query.currentPage = 1;
|
selectedKeys.value = [];
|
selectedRows.value = [];
|
loadList();
|
}
|
|
function handleSearch() {
|
if (!query.bankId) {
|
createMessage.warning('请先选择题库');
|
return;
|
}
|
query.currentPage = 1;
|
loadList();
|
}
|
|
function handlePageChange(page: number, pageSize: number) {
|
query.currentPage = page;
|
query.pageSize = pageSize;
|
loadList();
|
}
|
|
function handleOk() {
|
if (!query.bankId) {
|
createMessage.warning('请先选择题库');
|
return;
|
}
|
const rows = selectedRows.value.filter((r) => r.id && !excludeIds.value.has(r.id));
|
if (!rows.length) {
|
createMessage.warning('请选择试题');
|
return;
|
}
|
emit('confirm', rows);
|
closeModal();
|
}
|
</script>
|
|
<template>
|
<BasicModal
|
v-bind="$attrs"
|
title="选择试题"
|
width="900px"
|
destroy-on-close
|
@register="registerModal"
|
@ok="handleOk"
|
>
|
<div v-if="lockedType" class="picker-tip">
|
当前章节题型:<b>{{ typeLabel }}</b>,请选择题库后勾选试题
|
</div>
|
|
<a-form layout="inline" class="picker-query" @submit.prevent>
|
<a-form-item label="题库" required>
|
<a-select
|
v-model:value="query.bankId"
|
allow-clear
|
show-search
|
placeholder="请选择题库"
|
style="width: 220px"
|
:options="banks"
|
:field-names="{ label: 'fullName', value: 'id' }"
|
option-filter-prop="fullName"
|
@change="onBankChange"
|
/>
|
</a-form-item>
|
<a-form-item label="关键词">
|
<a-input
|
v-model:value="query.keyword"
|
allow-clear
|
placeholder="编号/题干"
|
style="width: 180px"
|
:disabled="!query.bankId"
|
@press-enter="handleSearch"
|
/>
|
</a-form-item>
|
<a-form-item>
|
<a-button type="primary" :disabled="!query.bankId" @click="handleSearch">查询</a-button>
|
</a-form-item>
|
</a-form>
|
|
<a-empty v-if="!query.bankId" description="请先选择题库" />
|
|
<a-table
|
v-else
|
size="small"
|
row-key="id"
|
:loading="loading"
|
:data-source="list"
|
:columns="columns"
|
:pagination="{
|
current: query.currentPage,
|
pageSize: query.pageSize,
|
total: query.total,
|
showSizeChanger: true,
|
onChange: handlePageChange,
|
}"
|
:row-selection="{
|
selectedRowKeys: selectedKeys,
|
onChange: onSelectChange,
|
getCheckboxProps: (record: QuestionEntity) => ({ disabled: rowDisabled(record) }),
|
}"
|
>
|
<template #bodyCell="{ column, record }">
|
<template v-if="column.key === 'questionType'">
|
{{ labelOfType(record.questionType) }}
|
</template>
|
<template v-else-if="column.key === 'stem'">
|
{{ stripHtml(record.stem) }}
|
<span v-if="rowDisabled(record)" class="picked">(已入卷)</span>
|
</template>
|
</template>
|
</a-table>
|
</BasicModal>
|
</template>
|
|
<style scoped>
|
.picker-tip {
|
margin-bottom: 12px;
|
color: #666;
|
font-size: 13px;
|
}
|
.picker-query {
|
margin-bottom: 12px;
|
}
|
.picked {
|
color: #999;
|
margin-left: 4px;
|
}
|
</style>
|