api.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import { StudentCheckItem } from "./types";
  2. import { useAppStore } from "@/store";
  3. import { randomCode } from "@/utils/tool";
  4. import axios from "axios";
  5. import Papa from "papaparse";
  6. const appStore = useAppStore();
  7. export const allCheckList = async (
  8. params: AllCheckFilter
  9. ): Promise<StudentCheckItem[]> => {
  10. // if (!params.isCheck && !params.imageName) {
  11. // return Promise.resolve([]);
  12. // }
  13. const fname = params.isCheck ? "check" : "success";
  14. const url = `${appStore.serverUrl}/${fname}.csv?${randomCode()}`;
  15. let students: StudentCheckItem[] = await fetchAndParseData(url);
  16. if (params.imageName) {
  17. students = students.filter((item) => item.imageName === params.imageName);
  18. }
  19. const cacheList = await fetchCacheList();
  20. const data = (students || []).map((item) => {
  21. return {
  22. ...item,
  23. checked: cacheList.includes(item.imageName),
  24. };
  25. });
  26. return Promise.resolve(data);
  27. };
  28. export const getExamNumberInfo = async (
  29. imageName: string
  30. ): Promise<DataCheckListResult> => {
  31. const url = `${appStore.serverUrl}/cache/${imageName}.csv?${randomCode()}`;
  32. const students = await fetchAndParseData(url).catch(() => []);
  33. if (!students || students.length === 0) {
  34. return Promise.resolve(null);
  35. }
  36. const item = students[0];
  37. const questions = (item.smda ? item.smda.split("|") : []).map((item) =>
  38. item.trim().replace(/[\.\?]/g, "")
  39. );
  40. return Promise.resolve({ ...students[0], questions });
  41. };
  42. export const fetchCacheList = async () => {
  43. try {
  44. const res = await axios.get(`${appStore.serverUrl}/cache/`);
  45. const parser = new DOMParser();
  46. const doc = parser.parseFromString(res.data, "text/html");
  47. const links = Array.from(doc.querySelectorAll("a"))
  48. .map((a) => a.getAttribute("href"))
  49. .filter((href) => href && /\/cache\/.*\.csv$/.test(href))
  50. .map((href) => {
  51. // Extract filename without extension
  52. const parts = href.split("/");
  53. const filenameWithExtension = parts[parts.length - 1];
  54. return filenameWithExtension.replace(".csv", "");
  55. });
  56. // console.log("缓存文件列表:", links);
  57. return links; // Return the extracted list
  58. } catch (err) {
  59. console.error("获取失败:", err);
  60. }
  61. };
  62. export const saveCheck = async (data: string[][], filename): Promise<any> => {
  63. const url = `${appStore.serverUrl}/upload?path=/cache`;
  64. return uploadCsvData(url, data, filename);
  65. };
  66. /**
  67. * 请求给定远程地址,获取文件流数据(CSV 或 JSON),并解析其内容。
  68. * @param remoteUrl 远程文件地址
  69. * @returns 解析后的数据
  70. */
  71. export const fetchAndParseData = async (remoteUrl: string): Promise<any> => {
  72. try {
  73. const response = await axios.get(remoteUrl, {
  74. responseType: "text", // Fetch as text to handle both JSON and CSV
  75. });
  76. const contentType = response.headers["content-type"];
  77. let data;
  78. if (contentType && contentType.includes("application/json")) {
  79. // Try parsing as JSON
  80. try {
  81. data = JSON.parse(response.data);
  82. } catch (error) {
  83. console.error("Error parsing JSON:", error);
  84. throw new Error("Failed to parse JSON data");
  85. }
  86. } else if (contentType && contentType.includes("text/csv")) {
  87. // Try parsing as CSV
  88. return new Promise((resolve, reject) => {
  89. Papa.parse(response.data, {
  90. header: true, // Assume header row
  91. skipEmptyLines: true,
  92. complete: (results) => {
  93. resolve(results.data);
  94. },
  95. error: (error: any) => {
  96. console.error("Error parsing CSV:", error);
  97. reject(new Error("Failed to parse CSV data"));
  98. },
  99. });
  100. });
  101. } else {
  102. // Attempt to guess format or throw error
  103. try {
  104. // Try JSON first
  105. data = JSON.parse(response.data);
  106. } catch (jsonError) {
  107. // If JSON fails, try CSV
  108. return new Promise((resolve, reject) => {
  109. Papa.parse(response.data, {
  110. header: true,
  111. skipEmptyLines: true,
  112. complete: (results) => {
  113. if (results.errors.length > 0) {
  114. console.error("CSV parsing errors:", results.errors);
  115. // If CSV parsing also has issues, reject
  116. reject(
  117. new Error(
  118. "Failed to parse data: Unknown format or invalid content"
  119. )
  120. );
  121. } else {
  122. resolve(results.data);
  123. }
  124. },
  125. error: (csvError: any) => {
  126. console.error("Error parsing CSV:", csvError);
  127. reject(
  128. new Error(
  129. "Failed to parse data: Could not determine format or invalid content"
  130. )
  131. );
  132. },
  133. });
  134. });
  135. }
  136. }
  137. return data;
  138. } catch (error) {
  139. console.error("Error fetching or parsing data:", error);
  140. throw new Error("Failed to fetch or parse data from the remote URL");
  141. }
  142. };
  143. /**
  144. * 构建 CSV 文件内容并上传到指定 URL。
  145. * @param uploadUrl 上传的目标 URL
  146. * @param data 要转换为 CSV 并上传的数据数组
  147. * @param filename 上传的文件名
  148. * @returns 上传结果
  149. */
  150. export const uploadCsvData = async (
  151. uploadUrl: string,
  152. data: string[][],
  153. filename: string = "upload.csv"
  154. ): Promise<any> => {
  155. try {
  156. // Convert data array to CSV string
  157. const csvString = Papa.unparse(data);
  158. // Create a Blob from the CSV string
  159. const blob = new Blob([csvString], { type: "text/csv;charset=utf-8;" });
  160. // Create FormData and append the Blob as a file
  161. const formData = new FormData();
  162. formData.append("file", blob, filename); // 'file' is a common field name for uploads, adjust if needed
  163. // Upload the FormData using axios
  164. const response = await axios.post(uploadUrl, formData, {
  165. headers: {
  166. "Content-Type": "multipart/form-data",
  167. },
  168. });
  169. return response.data; // Return the response from the server
  170. } catch (error) {
  171. console.error("Error building or uploading CSV data:", error);
  172. throw new Error("Failed to build or upload CSV data");
  173. }
  174. };