feat(aihr): complete phase two pilot readiness
This commit is contained in:
@@ -217,10 +217,11 @@ export function calibratePracticeSession(sessionId: string, data: PracticeCalibr
|
||||
});
|
||||
}
|
||||
|
||||
export function exportPracticePilotCsv(): Promise<Blob> {
|
||||
export function exportPracticePilotCsv(params: { startDate: string; endDate: string }): Promise<Blob> {
|
||||
return request({
|
||||
url: '/api/train/practice/export',
|
||||
method: 'get',
|
||||
params,
|
||||
responseType: 'blob'
|
||||
}) as unknown as Promise<Blob>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { resolvePilotDateWindow } from './pilot-export';
|
||||
|
||||
describe('resolvePilotDateWindow', () => {
|
||||
it('returns null when the Element Plus date range is cleared', () => {
|
||||
expect(resolvePilotDateWindow(null)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns an explicit start and end date only for a complete range', () => {
|
||||
expect(resolvePilotDateWindow(['2026-07-07', '2026-07-10'])).toEqual({
|
||||
startDate: '2026-07-07',
|
||||
endDate: '2026-07-10'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
export type PilotDateWindow = {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
|
||||
export function resolvePilotDateWindow(value: unknown): PilotDateWindow | null {
|
||||
if (!Array.isArray(value) || value.length !== 2) {
|
||||
return null;
|
||||
}
|
||||
const [startDate, endDate] = value;
|
||||
if (typeof startDate !== 'string' || typeof endDate !== 'string' || !startDate || !endDate) {
|
||||
return null;
|
||||
}
|
||||
return { startDate, endDate };
|
||||
}
|
||||
@@ -8,7 +8,17 @@
|
||||
<div class="head-actions">
|
||||
<span class="status-pill" :class="apiPill.className">{{ apiPill.text }}</span>
|
||||
<span class="status-pill" :class="flowPill.className">{{ flowPill.text }}</span>
|
||||
<el-button :loading="exportingPilot" @click="exportPilotCsv">导出试点CSV</el-button>
|
||||
<el-date-picker
|
||||
v-model="pilotDateRange"
|
||||
class="pilot-date-range"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
range-separator="至"
|
||||
start-placeholder="试点开始"
|
||||
end-placeholder="试点结束"
|
||||
unlink-panels
|
||||
/>
|
||||
<el-button :icon="Download" :loading="exportingPilot" :disabled="!pilotExportWindow" @click="exportPilotCsv">导出试点CSV</el-button>
|
||||
<el-button @click="router.push('/index')">返回首页</el-button>
|
||||
<el-button type="danger" :icon="Refresh" @click="resetPractice">重置流程</el-button>
|
||||
</div>
|
||||
@@ -292,7 +302,7 @@
|
||||
|
||||
<script setup name="TrainPractice" lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { Microphone, Refresh, Tickets, TrendCharts, UserFilled } from '@element-plus/icons-vue';
|
||||
import { Download, Microphone, Refresh, Tickets, TrendCharts, UserFilled } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useRouter } from 'vue-router';
|
||||
import FileSaver from 'file-saver';
|
||||
@@ -313,6 +323,7 @@ import {
|
||||
type PracticeScenarioResponse
|
||||
} from '@/api/aihr/practice';
|
||||
import { searchKnowledgeSop, type SopSearchResponse } from '@/api/aihr/sop';
|
||||
import { resolvePilotDateWindow } from './pilot-export';
|
||||
|
||||
type ChatRole = 'customer' | 'trainee' | 'coach';
|
||||
|
||||
@@ -461,6 +472,8 @@ const calibrationReason = ref('管理端人工校准');
|
||||
const calibrationSaving = ref(false);
|
||||
const calibrationMessage = ref('');
|
||||
const exportingPilot = ref(false);
|
||||
const pilotDateRange = ref<string[] | null>(null);
|
||||
const pilotExportWindow = computed(() => resolvePilotDateWindow(pilotDateRange.value));
|
||||
const promptCategoryOptions = ['practice', 'knowledge', 'supervision'];
|
||||
const defaultPromptForm = (): PromptTemplateForm => ({
|
||||
id: null,
|
||||
@@ -797,10 +810,16 @@ const saveCalibration = async () => {
|
||||
};
|
||||
|
||||
const exportPilotCsv = async () => {
|
||||
const dateWindow = pilotExportWindow.value;
|
||||
if (!dateWindow) {
|
||||
ElMessage.warning('请选择完整的试点批次日期');
|
||||
return;
|
||||
}
|
||||
const { startDate, endDate } = dateWindow;
|
||||
exportingPilot.value = true;
|
||||
try {
|
||||
const data = await exportPracticePilotCsv();
|
||||
FileSaver.saveAs(new Blob([data], { type: 'text/csv;charset=utf-8' }), 'aihr-practice-pilot.csv');
|
||||
const data = await exportPracticePilotCsv({ startDate, endDate });
|
||||
FileSaver.saveAs(new Blob([data], { type: 'text/csv;charset=utf-8' }), `aihr-practice-pilot-${startDate}-${endDate}.csv`);
|
||||
ElMessage.success('试点数据 CSV 已导出');
|
||||
} catch {
|
||||
ElMessage.error('试点数据导出失败,请检查权限');
|
||||
@@ -967,6 +986,10 @@ onMounted(() => {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pilot-date-range {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.flow-strip {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user