简述TypeScript 什么是 .map 文件,为什么/如何使用它?

在TypeScript(以及其他使用转译过程的语言)开发中,.map文件是源代码映射(Source Map)文件,它提供了转译后的代码(比如编译成JavaScript的TypeScript代码)与原始源代码之间的映射信息。这种映射使得在调试过程中,开发工具能够将运行时的代码关联回原始的TypeScript源代码,即使实际运行在浏览器或Node.js环境中的是转译后的JavaScript代码。

为什么使用.map文件?
  1. 调试友好:开发者可以直接在开发工具中调试TypeScript源代码,而不是转译后可能难以阅读和理解的JavaScript代码。这大大提高了调试效率。

  2. 错误追踪:当运行时发生错误时,错误堆栈可以直接指向TypeScript源代码中的具体位置,而不是指向转译后的JavaScript代码。这使得定位和修复问题更加直观快捷。

如何使用它?

在TypeScript中生成.map文件,你需要在tsconfig.json配置文件中启用sourceMap选项:

{
    "compilerOptions": {
        "sourceMap": true,
        ...
    }
}

当这个选项被设置为true,TypeScript编译器在编译TypeScript文件到JavaScript时,会为每个输出的JavaScript文件生成一个对应的.map文件。例如,如果你有一个example.ts文件,编译后你会得到example.jsexample.js.map

如何使用.map文件进行调试?
  • 在浏览器中:大多数现代浏览器的开发者工具都自动支持源代码映射。当你在浏览器中打开开发者工具并调试JavaScript代码时,如果存在有效的.map文件,浏览器会自动使用它们来显示原始的TypeScript代码。

  • 在Node.js中:对于Node.js应用,你可能需要使用源码映射支持库(如source-map-support)来确保堆栈跟踪正确指向TypeScript源文件。

通过使用.map文件,开发者可以更方便地调试和维护他们的应用,尤其是在涉及到复杂逻辑和大型项目时。这些文件在生产环境中通常不必要,因此在部署应用时,你可能会选择不包含它们,以减少发送到客户端的数据量。

类型“{ query: { campusIds: number[]; courseType?: number; subjectId?: number; grades: string[]; status: number; statusNotEqual: number; enrollStatus: number; enrollLimit: number; priceType: string; ... 16 more ...; size: number; }; }”的参数不能赋给类型“ClassGroupQuery”的参数。 类型“{ query: { campusIds: number[]; courseType?: number | undefined; subjectId?: number | undefined; grades: string[]; status: number; statusNotEqual: number; enrollStatus: number; enrollLimit: number; ... 17 more ...; size: number; }; }”缺少类型“ClassGroupQuery”的以下属性: grades, campusIds, status, statusNotEqual 及其他 20 项。ts-plugin(2345) 类型“{ query: { campusIds: number[]; view: number; subjectIds: number[]; teachTypes: number[]; courseTypes: number[]; grades: string[]; teacherIds: number[]; classRoomIds: number[]; timeRange: string[]; timeView: number; onlyShowHasClass: boolean; index: number; size: number; }; }”的参数不能赋给类型“ClassItemQuery”的参数。 类型“{ query: { campusIds: number[]; view: number; subjectIds: number[]; teachTypes: number[]; courseTypes: number[]; grades: string[]; teacherIds: number[]; classRoomIds: number[]; timeRange: string[]; timeView: number; onlyShowHasClass: boolean; index: number; size: number; }; }”缺少类型“ClassItemQuery”的以下属性: campusIds, view, subjectIds, teachTypes 及其他 9 项。ts-plugin(2345) 类型“never[] | PageResult<ClassItem>”上不存在属性“records”。 类型“never[]”上不存在属性“records” 这是我代码的报错 <!-- 补课 --> <template> <div class="page-content" v-loading="loading"> <el-card class="search-card"> <el-form-item label="上课日期:"> <el-radio-group class="custom-radio-group" @change="getData" v-model="query.timeType"> <el-radio-button label="不限" :value="-1" /> <el-radio-button label="本周" :value="1" /> <el-radio-button label="本月" :value="2" /> <el-radio-button label="上月" :value="3" /> <el-radio-button label="自定义" :value="4" /> </el-radio-group> </el-form-item> <el-divider border-style="dashed" class="query-split-line" /> <el-form-item label="补课状态:"> <el-radio-group class="custom-radio-group" @change="getData" v-model="query.makeupStatus"> <el-radio-button label="不限" :value="-1" /> <el-radio-button label="已补课" :value="1" /> <el-radio-button label="未补课" :value="0" /> </el-radio-group> </el-form-item> <el-divider border-style="dashed" v-if="showMore" class="query-split-line" /> <el-form-item label="更多筛选:" class="more-filter" v-show="showMore"> <el-select placeholder="授课模式" clearable v-model="query.teachType" @change="getData"> <el-option label="班课" :value="1" /> <el-option label="1对1" :value="2" /> </el-select> <CampusSelect placeholder="校区" multiple @change="getData" clearable v-model="query.campusIds" /> <GradeSelect placeholder="年级" multiple @change="getData" clearable v-model="query.grades" /> <SubjectSelect placeholder="科目" multiple @change="getData" clearable v-model="query.subjectIds" /> <CourseTypeSelect placeholder="课程类型" v-model="query.courseTypeIds" clearable multiple @change="getData" /> <TeacherSelect placeholder="请选择老师" @change="getData" clearable v-model="query.teacherId" filterable /> <TeacherSelect placeholder="请选择教务" @change="getData" clearable v-model="query.assistantId" filterable /> <DatePicker transfer type="daterange" placeholder="补课日期" :value="query.makeupDateRange" style="width: 200px" @on-change="onMakeupDateRangeChange" /> <el-select placeholder="上课时间段" /> <el-select placeholder="上课教室" @change="getData" /> </el-form-item> <el-divider border-style="dashed" class="cursor-pointer query-split-line" @click="showMore = !showMore"> {{ showMore ? "收起" : "更多筛选" }} <el-icon> <ArrowUp v-if="showMore" /> <ArrowDown v-else /> </el-icon> </el-divider> </el-card> <el-card> <div class="flex justify-between text-sm mb4"> <div class="flex gap-1"> 共 <strong>{{ total }}</strong> 条班级记录 </div> <!--查询--> <div class="flex gap-3"> <el-input placeholder="输入学生关键字查询" v-model="input" @change="getData" class="input-with-select"> <template #prepend> <el-select v-model="selectType" style="width: 80px"> <el-option label="课程" value="course" /> <el-option label="班级" value="class" /> <el-option label="学生" value="student" /> </el-select> </template> </el-input> <!--批量操作--> <el-dropdown @command="(command: string) => handleMakeupAction(selectedRows, command)"> <el-button class="batch-action-btn"> 批量操作 <el-icon><arrow-down /></el-icon> </el-button> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click="handleBatchMakeup">直接补课</el-dropdown-item> <el-dropdown-item command="in-class">插班补课</el-dropdown-item> <el-dropdown-item command="cancel">取消补课</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </div> </div> <SelectionTable ref="tableRef" :data="tableData" header-cell-class-name="table-header" border v-model="selectedRows"> <el-table-column label="上课日期" show-overflow-tooltip min-width="180"> <template #default="scope">{{ moment(scope.row.date).format("YYYY-MM-DD ") }}</template> </el-table-column> <el-table-column label="上课时间" show-overflow-tooltip min-width="180" prop="classItem.classTime" /> <el-table-column label="ID" show-overflow-tooltip min-width="100" prop="student.id" /> <el-table-column label="班级" show-overflow-tooltip min-width="300" prop="classGroup.name" /> <el-table-column label="学员" show-overflow-tooltip min-width="100" prop="student.name" /> <el-table-column label="老师" show-overflow-tooltip min-width="100"> <template #default="scope"> {{ getNames(scope.row.classItem.teachers).join(",") }} </template> </el-table-column> <el-table-column label="教务" show-overflow-tooltip min-width="100"> <template #default="scope"> {{ getNames(scope.row.classGroup.assistants).join(",") }} </template> </el-table-column> <el-table-column label="考勤状态" width="100"> <template #default="scope"> <el-tag v-if="scope.row.classItem.status == 1" type="success">出勤</el-tag> <el-tag v-else-if="scope.row.classItem.status == 2" type="warning">请假</el-tag> <el-tag v-else-if="scope.row.classItem.status == 3" type="danger">缺勤</el-tag> <el-tag v-else type="info">未知</el-tag> </template> </el-table-column> <el-table-column label="补课状态" width="100"> <template #default="scope"> <el-tag v-if="scope.row.makeupStatus == 0" type="info">未补课</el-tag> <el-tag v-else-if="scope.row.makeupStatus == 1" type="success">已补课</el-tag> </template> </el-table-column> <el-table-column label="补课类型" width="100"> <template #default="scope"> <el-tag v-if="scope.row.makeupType == MakeupType.Direct" type="success">直接补课</el-tag> <el-tag v-else-if="scope.row.makeupType == MakeupType.MakeupInOtherClass" type="success">插班补课</el-tag> </template> </el-table-column> <el-table-column label="补课老师" width="100"> <template #default="scope"> {{ getNames([scope.row.teacherId]).join(",") }} </template> </el-table-column> <el-table-column label="补课日期" width="100" prop="makeupDate"> <template #default="scope"> {{ scope.row.makeupDate ? moment(scope.row.makeupDate).format("YYYY-MM-DD") : "" }} </template> </el-table-column> <el-table-column label="备注" show-overflow-tooltip width="120" prop="remark" /> <el-table-column label="操作" width="80" fixed="right"> <template #default="{ row }"> <el-dropdown trigger="click"> <span class="more-action"> 更多 <el-icon class="arrow-icon"><ArrowDown /></el-icon> </span> <template #dropdown> <el-dropdown-menu> <el-dropdown-item v-if="row.makeupStatus === 0" @click="handleMakeup(row)">直接补课</el-dropdown-item> <el-dropdown-item v-if="row.makeupStatus === 0" @click="handleMakeupAction">插班补课</el-dropdown-item> <el-dropdown-item v-if="row.makeupStatus === 1" @click="cancelMakeup([row])">取消补课</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </template> </el-table-column> </SelectionTable> </el-card> <!--直接补课对话框--> <el-dialog v-model="directMakeup" title="直接补课" width="600px" destroy-on-close> <el-form :model="form" label-position="top" ref="formRef" :rules="rules"> <el-form-item label="补课老师" prop="teacherId"> <TeacherSelect v-model="form.teacherId" filterable clearable placeholder="请选择老师" /> </el-form-item> <el-form-item label="补课日期" prop="makeupDate"> <el-date-picker v-model="form.makeupDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择补课日期" style="width: 40%" /> </el-form-item> <el-form-item label="补课时间段" prop="makeUpTimeRange"> <TimePicker confirm type="timerange" :value="form.makeUpTimeRange" @on-change="onTimeRangeChange" placeholder="请选择上课时段" :steps="[1, 15]" :format="timeFormat" /> </el-form-item> <el-form-item label="备注" prop="remark"> <el-input v-model="form.remark" placeholder="请填写备注" type="textarea" /> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" :disabled="loading" @click="handleConfirmMakeup">确定</el-button> <el-button @click="handleDialogClose">取消</el-button> </div> </template> </el-dialog> <!--插班补课对话框--> <el-dialog v-model="inclassMakeup" title="插班补课" width="1000px"> <el-alert title="只支持同课程下的班级安排补课,每个月3号过后,不能插入上个月及以前月份的班级" type="warning" class="mb-4" /> <div class="filters mb-4 flex gap-4"> <!-- 校区下拉选择 --> <CampusSelect placeholder="请选择" v-model="classItemQuery.campusIds" @change="campusChange" style="width: 90px" /> <!-- 班级下拉选择 --> <el-select placeholder="请选择班级" v-model="selectedClassGroupId" filterable clearable @change="classGroupChange" style="width: 200px"> <el-option v-for="item in classGroupList" :key="item.id" :label="item.name" :value="item.id" /> </el-select> <!-- 日期选择 --> <DatePicker type="daterange" placeholder="补课日期" :value="classItemQuery.timeRange" onchange="onInClassRange" style="width: 200px" /> </div> <!--插班补课表格--> <el-table :data="classItemList" border header-cell-class-name="table-header" @selection-change="handleClassItemSelectionChange"> <el-table-column type="selection" width="55" align="center" :selectable="() => true" :reserve-selection="false"> <template #header> <!-- 空 header,隐藏全选框 --> </template> </el-table-column> <el-table-column label="班级" show-overflow-tooltip min-width="180" prop="classGroup.name" /> <el-table-column label="上课时间" show-overflow-tooltip min-width="180"> <template #default="scope">{{ moment(scope.row.date).format("YYYY-MM-DD ") }}{{ scope.row.startTime }} - {{ scope.row.endTime }}</template> </el-table-column> <el-table-column label="老师" show-overflow-tooltip min-width="100"> <template #default="scope"> {{ getNames(scope.row.teachers).join(",") }} </template> </el-table-column> <el-table-column label="教室" show-overflow-tooltip min-width="100" prop="classRoomName" /> </el-table> <template #footer> <div class="dialog-footer"> <el-button type="primary" :disabled="loading" @click="submitMakeupInclass">确定</el-button> <el-button @click="inclassMakeup = false">取消</el-button> </div> </template> </el-dialog> </div> </template> <script setup lang="ts"> import type { ElTable, FormInstance } from "element-plus"; import moment from "moment"; import { useUserStore } from "@/store"; import { Makeup, MakeupQuery, MakeupStatus, MakeupType, rules } from "@/models/enroll/Makeup"; import { useRouter } from "vue-router"; import { MakeupAPI } from "@/api/MakeupApi"; import { DatePicker, Row } from "view-ui-plus"; import { ClassItem, ClassGroup } from "@/models/routine/ClassGroup"; import { RoutineAPI } from "@/api/RoutineAPI"; import { ClassItemQuery } from "@/models/routine/scheduleTable/Query"; import { ClassGroupQuery } from "@/models/routine/Query"; import { TimePicker } from "view-ui-plus"; import ClassGroupList from "../ClassGroupList.vue"; const selectedClassGroupId = ref<number>(); const classItemList = ref<ClassItem[]>([]); const classGroupList = ref<ClassGroup[]>([]); const userStore = useUserStore(); const directMakeup = ref(false); const inclassMakeup = ref(false); const timeFormat: any = "HH:mm"; // 状态管理 const loading = ref(false); const showMore = ref(true); const tableData = ref<Makeup[]>([]); const total = ref(0); const input = ref(""); const selectType = ref("student"); const tableRef = ref(); const selectedRows = ref<Makeup[]>([]); const query = ref<MakeupQuery>(new MakeupQuery()); const classItemQuery = ref<ClassItemQuery>(new ClassItemQuery()); const classGroupQuery = ref<ClassGroupQuery>(new ClassGroupQuery()); //插班补课表格复选框 const selectedClassItems = ref<ClassItem[]>([]); function handleClassItemSelectionChange(rows: ClassItem[]) { selectedClassItems.value = rows; } //直接补课表单 const formRef = ref<FormInstance>(); const form = ref(new Makeup()); //重置直接补课表单,清除验证状态 function resetForm() { form.value = new Makeup(); nextTick(() => { if (formRef.value) { formRef.value.clearValidate(); // 清除验证状态 } }); } const getClassGroupList = async () => { // 确保campusIds是数组格式 const queryParams = { query: { ...new ClassGroupQuery(), campusIds: Array.isArray(classItemQuery.value.campusIds) ? classItemQuery.value.campusIds : [classItemQuery.value.campusIds].filter(Boolean), }, }; const res = await RoutineAPI.queryClassGroup(queryParams); classGroupList.value = res?.records || []; }; function campusChange() { getClassGroupList(); getClassItemList(); } const classGroupChange = async () => { if (selectedClassGroupId.value) { loading.value = true; const items = await RoutineAPI.getClassItemByGroupId(selectedClassGroupId.value); classItemList.value = items || []; loading.value = false; } else { getClassItemList(); } }; //直接补课对话框关闭处理 function handleDialogClose() { resetForm(); directMakeup.value = false; } function onTimeRangeChange(range: string[]) { form.value.makeUpTimeRange = range[0] ? range : []; } //提交直接补课 async function handleConfirmMakeup() { formRef.value?.validate(async (pass) => { if (pass) { if (form.value.id) { loading.value = true; form.value.makeupStatus = MakeupStatus.Completed; const res = await MakeupAPI.updateMakeup(form.value); loading.value = false; if (res) { handleDialogClose(); getData(); } } else { const datas = selectedRows.value.map((item) => { item.makeupStatus = MakeupStatus.Completed; const data = new Makeup(); Object.assign(data, item); data.makeupType = form.value.makeupType; data.makeUpTimeRange = form.value.makeUpTimeRange; data.makeupDate = item.makeupDate; data.teacherId = form.value.teacherId; data.makeupDate = form.value.makeupDate; data.makeupStatus = item.makeupStatus; data.remark = form.value.remark; return data; }); loading.value = true; const res = await MakeupAPI.updateMakeupBatch(datas); loading.value = false; if (res) { handleDialogClose(); getData(); } } } }); } /** 提交插班补课 */ function submitMakeupInclass() {} //单个记录直接补课 function handleMakeup(row: Makeup) { // 创建行数据的深拷贝 const rowData = JSON.parse(JSON.stringify(row)); //bug:更多按钮直接补课老师选择器默认展示teacherId值0 // 修复:如果 teacherId 为 0,设置为 undefined if (rowData.teacherId === 0) { rowData.teacherId = undefined; } form.value = rowData; form.value.makeupType = MakeupType.Direct; directMakeup.value = true; } //批量处理直接补课 function handleBatchMakeup() { if (selectedRows.value.length === 0) { ElMessage.warning("请先选择需要补课的数据"); return; } if (selectedRows.value.some((item) => item.makeupStatus !== 0)) { ElMessage.warning("请选择未补课的数据"); return; } form.value = new Makeup(); form.value.makeupType = MakeupType.Direct; directMakeup.value = true; } //取消补课 async function cancelMakeup(rows: Makeup[]) { // 状态检查 if (rows.length === 0) { ElMessage.warning("请先选择需要取消补课的数据"); return; } const makeStatuses = new Set(rows.map((r) => r.makeupStatus)); if (makeStatuses.size > 1) { ElMessage.warning("请选择相同补课状态的记录进行批量操作"); return; } const makeStatus = rows[0].makeupStatus; if (makeStatus !== 1) { ElMessage.warning("只有已补课状态的记录才允许取消补课"); return; } // 确认提示(根据记录数量显示不同提示) try { await ElMessageBox.confirm(rows.length === 1 ? "确定取消该补课记录吗?" : `确定取消选中的${rows.length}条补课记录吗?`, "取消确认", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }); } catch (cancel) { ElMessage.info("操作取消"); return; } // 执行取消操作 loading.value = true; const ids = rows.map((r) => r.id); const cancelSuccess = await MakeupAPI.cancelMakeup(ids); loading.value = false; if (cancelSuccess) { getData(); // 刷新数据 // 清空选择(如果是批量操作) if (rows.length > 1) { selectedRows.value = []; tableRef.value?.clearSelection(); } } } //补课类型选择 const handleMakeupAction = async (rows: Makeup[], action: string) => { if (action === "cancel") { await cancelMakeup(rows); return; } else { if (action === "direct") { selectedRows.value = rows; directMakeup.value = true; return; } else if (action === "in-class") { selectedRows.value = rows; inclassMakeup.value = true; getClassItemList(); } } loading.value = false; }; async function getData() { loading.value = true; const res = await MakeupAPI.queryMakeup(query.value); loading.value = false; tableData.value = Array.isArray(res?.records) ? res.records : []; total.value = res?.total || 0; nextTick(() => { tableRef.value.toggleSelection(selectedRows.value); }); } async function getClassItemList() { loading.value = true; // 如果有选中班级,使用getClassItemByGroupId API if (selectedClassGroupId.value) { const items = await RoutineAPI.getClassItemByGroupId(selectedClassGroupId.value); classItemList.value = items || []; } // 如果没有选中班级但有选中校区,使用queryClassItem API else if (classItemQuery.value.campusIds && classItemQuery.value.campusIds.length > 0) { // 确保请求格式正确 const queryParams = { query: { ...classItemQuery.value, campusIds: Array.isArray(classItemQuery.value.campusIds) ? classItemQuery.value.campusIds : [classItemQuery.value.campusIds].filter(Boolean), }, }; const apiResponse = await RoutineAPI.queryClassItem(queryParams); classItemList.value = apiResponse?.records || []; } // 如果既没有选中班级也没有选中校区,清空列表 else { classItemList.value = []; } loading.value = false; } function getNames(row: string[]) { return row?.map((r) => userStore.userNameMap.get(parseInt(r))) ?? []; } function onMakeupDateRangeChange(range: string[]) { query.value.makeupDateRange = range[0] ? [range[0] + " 00:00:00", range[1] + " 23:59:59"] : []; getData(); } function onInClassRange(range: string[]) { classItemQuery.value.timeRange = range[0] ? [range[0]] : []; getClassItemList(); } onMounted(() => { getData(); }); </script> <style lang="scss" scoped></style> 这是我的整体代码,如果你需要其他接口或者对象的代码我会后续再给你
最新发布
08-22
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值