From 8534025c45b4736975730678b9ccf23570a75f95 Mon Sep 17 00:00:00 2001
From: liuyu <yu.liu@cqgbkj.com>
Date: 星期二, 22 九月 2026 09:25:48 +0800
Subject: [PATCH] feat(tms): 新增试卷、培训任务、个人任务页面
---
apps/jnpf-web-apps-main/src/views/x/tms/paper/components/QuestionPicker.vue | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 229 insertions(+), 0 deletions(-)
diff --git a/apps/jnpf-web-apps-main/src/views/x/tms/paper/components/QuestionPicker.vue b/apps/jnpf-web-apps-main/src/views/x/tms/paper/components/QuestionPicker.vue
new file mode 100644
index 0000000..8a3f316
--- /dev/null
+++ b/apps/jnpf-web-apps-main/src/views/x/tms/paper/components/QuestionPicker.vue
@@ -0,0 +1,229 @@
+<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>
--
Gitblit v1.8.0