liuyu
21 小时以前 93c133349a5ccd7a328371fa113dce69d5611f21
apps/jnpf-web-apps-main/src/views/x/tms/examScore/index.vue
@@ -1,23 +1,28 @@
<script lang="ts" setup>
import type { ActionItem, BasicColumn } from '@jnpf/ui/vxeTable';
import type { ExamScoreSummaryItem } from './types';
import type { ExamScoreDetailRow, ExamScoreSummaryItem } from './types';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useMessage } from '@jnpf/hooks';
import { BasicVxeTable, TableAction, useVxeTable } from '@jnpf/ui/vxeTable';
import { getExamScoreList } from '#/api/x/tms/examScore';
import { exportExamScores, getExamScoreList } from '#/api/x/tms/examScore';
import { TMS_BTN } from '#/views/x/tms/shared/ui';
import '#/views/x/tms/shared/page.css';
defineOptions({ name: 'TmsExamScore' });
const router = useRouter();
const { createMessage } = useMessage();
const exporting = ref(false);
const columns: BasicColumn[] = [
  { title: '培训主题', dataIndex: 'taskSubject', minWidth: 240 },
  { title: '培训编号', dataIndex: 'taskNo', width: 140 },
  { title: '试卷名称', dataIndex: 'taskSubject', minWidth: 240 },
  { title: '试卷编号', dataIndex: 'taskNo', width: 180 },
  {
    title: '试卷名称',
    dataIndex: 'paperName',
@@ -63,9 +68,9 @@
      },
      {
        field: 'taskNo',
        label: '培训编号',
        label: '试卷编号',
        component: 'Input',
        componentProps: { placeholder: '请输入培训编号', submitOnPressEnter: true },
        componentProps: { placeholder: '请输入试卷编号', submitOnPressEnter: true },
      },
    ],
  },
@@ -78,24 +83,90 @@
});
async function fetchList(params: Record<string, any>) {
  return { data: await getExamScoreList(params) };
  const page = await getExamScoreList(params);
  return {
    data: {
      list: Array.isArray(page?.list) ? page.list : [],
      pagination: page?.pagination || { total: 0 },
    },
  };
}
function handleDetail(record: ExamScoreSummaryItem) {
  router.push(`/tms/examScore/detail/${record.id}`);
}
function handleExport() {
  const rows = getSelectRows?.() || [];
  if (!rows.length) {
async function handleExport() {
  const selected = (getSelectRows?.() || []) as ExamScoreSummaryItem[];
  if (!selected.length) {
    createMessage.warning('请先勾选要导出的考试记录');
    return;
  }
  createMessage.success(`已选择 ${rows.length} 条(导出接口联调后生效)`);
  if (exporting.value) return;
  exporting.value = true;
  try {
    const rows = await exportExamScores(selected.map((item) => item.id));
    if (!rows?.length) {
      createMessage.warning('所选试卷暂无已交卷成绩');
      return;
    }
    downloadScoreFile(rows, selected);
    createMessage.success(`已导出 ${rows.length} 条考生成绩`);
  } catch (e: any) {
    createMessage.error(e?.message || '导出失败');
  } finally {
    exporting.value = false;
  }
}
function passText(flag?: string) {
  if (flag === '1') return '合格';
  if (flag === '0') return '不合格';
  return '-';
}
function xmlCell(value: unknown) {
  const text = value == null ? '' : String(value);
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function scoreFileName(selected: ExamScoreSummaryItem[]) {
  const names = [...new Set(selected.map((item) => (item.taskSubject || '').trim()).filter(Boolean))];
  const raw = names.length === 1 ? `考试成绩_${names[0]}` : '考试成绩';
  return `${raw.replace(/[\\/:*?"<>|\r\n]/g, '_')}.xls`;
}
function downloadScoreFile(rows: ExamScoreDetailRow[], selected: ExamScoreSummaryItem[]) {
  const header = ['试卷名称', '试卷编号', '考生', '考试开始时间', '考试结束时间', '成绩', '及格分', '是否合格'];
  const body = rows.map((row) => [
    row.taskSubject,
    row.taskNo,
    row.userName || row.userId,
    row.examStart || '',
    row.examEnd || '',
    row.score,
    row.passScore,
    passText(row.passFlag),
  ]);
  const widths = [180, 160, 100, 150, 150, 60, 60, 80];
  const cols = widths.map((width) => `<Column ss:Width="${width}"/>`).join('');
  const toRow = (cells: unknown[]) =>
    `<Row>${cells.map((cell) => `<Cell><Data ss:Type="String">${xmlCell(cell)}</Data></Cell>`).join('')}</Row>`;
  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<Worksheet ss:Name="考试成绩"><Table>${cols}${toRow(header)}${body.map(toRow).join('')}</Table></Worksheet>
</Workbook>`;
  const blob = new Blob([xml], { type: 'application/vnd.ms-excel;charset=utf-8' });
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = scoreFileName(selected);
  link.click();
  URL.revokeObjectURL(link.href);
}
function getTableActions(record: ExamScoreSummaryItem): ActionItem[] {
  return [{ label: '考试详情', onClick: handleDetail.bind(null, record) }];
  return [{ label: TMS_BTN.detail, onClick: handleDetail.bind(null, record) }];
}
</script>
@@ -106,7 +177,8 @@
        <BasicVxeTable @register="registerTable">
          <template #tableTitle>
            <a-space>
              <a-button type="primary" @click="handleExport">导出考试</a-button>
              <a-button type="primary" :loading="exporting" @click="handleExport">{{ TMS_BTN.export }}</a-button>
              <span class="tms-toolbar-hint">按场次汇总成绩,可导出或进入明细</span>
            </a-space>
          </template>
          <template #paperName="{ record }">