common.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. export function pxToMm(value) {
  2. // var inch = value / getDPI()[0];
  3. let scale=1754/297;
  4. // var c_value = inch * 25.4;
  5. let result=value/scale;
  6. return parseFloat(result.toFixed(4));//保留四位小数
  7. }
  8. // /**
  9. // * 像素转换成毫米
  10. // * @param {number} px - 输入的像素值
  11. // * @returns {number} - 转换后的毫米值,保留四位小数
  12. // */
  13. // export function pxToMm(px) {
  14. // if (px) {
  15. // const DPI = getDPI(); // 获取当前设备的 DPI
  16. // const MM_PER_INCH = 25.4; // 每英寸毫米数
  17. // const mmValue = (px * MM_PER_INCH) / DPI;
  18. // return parseFloat(mmValue.toFixed(4)); // 保留四位小数
  19. // } else {
  20. // console.log("px", px);
  21. // return 0;
  22. // }
  23. // }
  24. // 保留整数 小数 四舍五入
  25. export function mmToPx(num) {
  26. if(num)
  27. {
  28. // console.log("num",num);
  29. // console.log("转换后",Math.round(1754/297*num));
  30. // let scale=1754/297;
  31. // let result=(scale*num).toFixed(4);
  32. // console.log("scale",scale,result);
  33. let scale=1754/297;
  34. return parseFloat((scale*num).toFixed(4)); //高 150dpi 1754 96dpi:1120 1754*2480
  35. // return Math.round(1754/297*num)
  36. }
  37. else
  38. {
  39. console.log("num",num);
  40. return 0;
  41. }
  42. }
  43. //获取考试背景颜色值
  44. export function getExamBackgroundColor(type){
  45. const examTypesColor = {
  46. "1": '#F56C6C',//期末
  47. "2": '#3ba272',//期中
  48. "3": '#ea7acb',//模拟
  49. "4": '#fac858',//月考
  50. "5": '#995fb3',//周测
  51. "6": '#72C0DD',//平时
  52. };
  53. const result = examTypesColor[type];
  54. return result;
  55. }
  56. // 获取分析报告成绩分析中默认的20个颜色
  57. export function getScorePerformanceAnalysis() {
  58. return [
  59. "#5470C6",
  60. "#3BA272",
  61. "#EE6666",
  62. "#FAC858",
  63. "#995FB3",
  64. "#72C0DD",
  65. "#90CB75",
  66. "#EA7ACB",
  67. "#FC8451",
  68. "#65789B",
  69. "#178BD3",
  70. "#26A616",
  71. "#A6129E",
  72. "#D3A141",
  73. "#234098",
  74. "#047640",
  75. "#B43535",
  76. "#848BDC",
  77. "#D6AF83",
  78. "#84313D",
  79. ]
  80. }
  81. // 文本截断:超过指定长度显示省略号
  82. export function truncateText(text, maxLength) {
  83. if (!text) return '';
  84. if (text.length > maxLength) {
  85. return text.substring(0, maxLength) + '...';
  86. }
  87. return text;
  88. }
  89. // 去掉小数点后面为0的 如果不为0 最多只保留2位小数
  90. export function getFormatNumber(numberStr) {
  91. // 尝试将字符串转为数字
  92. const num = parseFloat(numberStr);
  93. // 检查是否为NaN(非数字情况)
  94. if (isNaN(num)) {
  95. return numberStr;
  96. }
  97. // 检查是否为整数(小数点后全为0)
  98. if (num % 1 === 0) {
  99. return num.toString();
  100. }
  101. // 保留最多2位小数,并去除末尾的0
  102. let formatted = num.toFixed(2);
  103. // 如果小数点后都是0,则返回整数形式
  104. if (formatted.endsWith('.00')) {
  105. return formatted.split('.')[0];
  106. }
  107. // 去除末尾的0(如1.50 → 1.5)
  108. formatted = formatted.replace(/\.?0+$/, '');
  109. return formatted;
  110. }
  111. /**
  112. * 格式化时间,输出:xx月xx日 时:分,去除年份、秒
  113. * @param {string|number|null} time
  114. * @returns {string}
  115. */
  116. export function formatToMonthDayHM(time) {
  117. if (time == null || time === '') {
  118. return ''
  119. }
  120. const str = String(time).trim().replace('T', ' ')
  121. const match = str.match(/^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{2}:\d{2}))?(?::\d{2})?/)
  122. if (!match) {
  123. return str
  124. }
  125. const month = match[2]
  126. const day = match[3]
  127. const hm = match[4]
  128. if (hm) {
  129. return `${month}-${day} ${hm}`
  130. }
  131. return `${month}-${day}`
  132. }
  133. /**
  134. * 去掉后端时间中的秒,页面只展示「年月日 时分」
  135. * 支持:2026-08-27 16:44:00、2026-08-27T16:44:59、2026-08-27
  136. */
  137. export function formatTimeWithoutSeconds(time) {
  138. if (time == null || time === '') {
  139. return ''
  140. }
  141. const str = String(time).trim().replace('T', ' ')
  142. const match = str.match(/^(\d{4}-\d{2}-\d{2})(?:\s+(\d{2}:\d{2}))?(?::\d{2})?/)
  143. if (!match) {
  144. return str
  145. }
  146. if (match[2]) {
  147. return `${match[1]} ${match[2]}`
  148. }
  149. return match[1]
  150. }
  151. /**
  152. * 开始时间传给后端:补写死的 00 秒
  153. * 例:2026-08-27 16:44 → 2026-08-27 16:44:00
  154. */
  155. export function toApiStartTime(time) {
  156. const base = formatTimeWithoutSeconds(time)
  157. if (!base) {
  158. return ''
  159. }
  160. if (/^\d{4}-\d{2}-\d{2}$/.test(base)) {
  161. return `${base} 00:00:00`
  162. }
  163. return `${base}:00`
  164. }
  165. /**
  166. * 结束时间传给后端:补写死的 59 秒
  167. * 例:2026-08-27 17:30 → 2026-08-27 17:30:59
  168. */
  169. export function toApiEndTime(time) {
  170. const base = formatTimeWithoutSeconds(time)
  171. if (!base) {
  172. return ''
  173. }
  174. if (/^\d{4}-\d{2}-\d{2}$/.test(base)) {
  175. return `${base} 23:59:59`
  176. }
  177. return `${base}:59`
  178. }
  179. /**
  180. * 剩余时间:结束时间减去当前时间,精确到分钟。
  181. * 满一天显示「X天X小时X分」,不足一天显示「X小时X分」或「X分」。
  182. * 已过期返回 null。
  183. *
  184. * @param {string|Date} endTime - 结束时间
  185. * @returns {string|null}
  186. */
  187. export function remainingDays(endTime) {
  188. if (!endTime) return null
  189. const end = new Date(typeof endTime === 'string' ? String(endTime).replace(/-/g, '/') : endTime)
  190. if (isNaN(end.getTime())) return null
  191. const diffMs = end.getTime() - Date.now()
  192. if (diffMs <= 0) return null
  193. const MS_PER_MINUTE = 60 * 1000
  194. const MS_PER_HOUR = 60 * MS_PER_MINUTE
  195. const MS_PER_DAY = 24 * MS_PER_HOUR
  196. const days = Math.floor(diffMs / MS_PER_DAY)
  197. const hours = Math.floor((diffMs % MS_PER_DAY) / MS_PER_HOUR)
  198. const minutes = Math.floor((diffMs % MS_PER_HOUR) / MS_PER_MINUTE)
  199. if (days > 0) {
  200. return `${days}天${hours}小时${minutes}分`
  201. }
  202. if (hours > 0) {
  203. return `${hours}小时${minutes}分`
  204. }
  205. if (minutes > 0) {
  206. return `${minutes}分`
  207. }
  208. return '不足1分'
  209. }
  210. //时间
  211. export function unioformDateTransform(date, param = "time"){
  212. if (!date) return ''
  213. const d = new Date(date)
  214. // 判断日期是否合法
  215. if (isNaN(d.getTime())) return ''
  216. const year = d.getFullYear()
  217. // 月份从0开始,+1,不足两位补0
  218. const month = String(d.getMonth() + 1).padStart(2, '0')
  219. const day = String(d.getDate()).padStart(2, '0')
  220. const hours = String(d.getHours()).padStart(2, '0')
  221. const minutes = String(d.getMinutes()).padStart(2, '0')
  222. const seconds = String(d.getSeconds()).padStart(2, '0')
  223. const days = `${year}-${month}-${day}`
  224. const timeStr = `${hours}:${minutes}:${seconds}`
  225. switch (param) {
  226. case "day":
  227. return days
  228. case "time":
  229. return `${days} ${timeStr}`
  230. default:
  231. return `${days} ${timeStr}`
  232. }
  233. }
  234. export const fileSizeConvert = (byte)=>{
  235. let kbSize = 0
  236. let mbSize = 0
  237. let gbSize = 0
  238. kbSize = (byte / 1024)
  239. if(kbSize > 1024){
  240. mbSize = kbSize / 1024
  241. if(mbSize > 1024){
  242. gbSize = mbSize / 1024
  243. return gbSize.toFixed(2) + 'GB'
  244. }else {
  245. return mbSize.toFixed(2) + 'MB'
  246. }
  247. }else{
  248. return kbSize.toFixed(2) + 'KB'
  249. }
  250. }
  251. // 计算文件大小方法,如果文件可以算出 MB,就返回 MB,否则返回 KB
  252. export const getFileSize = (fileSize) => {
  253. if (!fileSize) return '0 KB'
  254. if (fileSize >= 1024 * 1024) {
  255. fileSize = Math.round(fileSize / (1024 * 1024))
  256. return `${fileSize} MB`
  257. } else {
  258. fileSize = Math.round(fileSize / 1024)
  259. return `${fileSize} KB`
  260. }
  261. }
  262. //部门及部门下的人
  263. export const getDeptTreeData = (list) => {
  264. if (!list || list.length === 0) return []
  265. const arr = []
  266. list.forEach((one,oneIndex) => {
  267. const { name, id, groupVOS } = one
  268. const objectOne = {
  269. label: name,
  270. id: `${id}_${oneIndex}`,
  271. children: []
  272. }
  273. arr.push(objectOne)
  274. if (groupVOS && groupVOS.length) {
  275. groupVOS.forEach((two,twoIndex) => {
  276. const { groupId, groupName, groupUserVOS } = two
  277. const objectTwo = {
  278. label: groupName,
  279. id: `${groupId}_${oneIndex}_${twoIndex}`,
  280. children: []
  281. }
  282. objectOne.children.push(objectTwo)
  283. if (groupUserVOS && groupUserVOS.length) {
  284. groupUserVOS.forEach((three, threeIndex) => {
  285. const { teacherName, teacherId, userAccount, canDel, selected } = three
  286. objectTwo.children.push({
  287. id: `${one.id}_${two.groupId}_${teacherId}_${threeIndex}`,
  288. label: `${teacherName}(${userAccount})`,
  289. targetType: 1,
  290. targetName: teacherName,
  291. targetId: teacherId,
  292. studentCode: null,
  293. roleCode: null,
  294. roleName: null,
  295. gradeCode: null,
  296. gradeName: null,
  297. subjectCode: null,
  298. subjectName: null,
  299. classIdCode: null,
  300. className: null,
  301. classType: null,
  302. classTypeName: null,
  303. departmentId: one.id,
  304. departmentName: one.name,
  305. departmentGroupId: two.groupId,
  306. departmentGroupName: two.groupName,
  307. disabled: !canDel,
  308. selected,
  309. checked:selected==1?'checked':''
  310. })
  311. })
  312. }
  313. })
  314. }
  315. })
  316. return arr
  317. }
  318. export const subjectTreeData = (list) => {
  319. if (!list || list.length === 0) return []
  320. const arr = []
  321. list.forEach(one => {
  322. const { subjectName, subjectCode, gradePersonVos } = one
  323. const objectOne = {
  324. label: subjectName,
  325. id: subjectCode,
  326. children: []
  327. }
  328. arr.push(objectOne)
  329. if (gradePersonVos && gradePersonVos.length) {
  330. gradePersonVos.forEach(two => {
  331. const { gradeName, gradeCode, personVoList } = two
  332. const objectTwo = {
  333. label: gradeName,
  334. id: `${subjectCode}_${gradeCode}`,
  335. children: []
  336. }
  337. objectOne.children.push(objectTwo)
  338. if (personVoList && personVoList.length) {
  339. personVoList.forEach((three, threeIndex) => {
  340. const {
  341. showName,
  342. teacherName,
  343. teacherId,
  344. userAccount,
  345. roleCode,
  346. roleName,
  347. gradeCode,
  348. gradeName,
  349. classIdCode,
  350. className,
  351. canDel,
  352. selected
  353. } = three
  354. objectTwo.children.push({
  355. id: `${one.subjectCode}_${two.gradeCode}_${teacherId}_${threeIndex}`,
  356. label: `${showName}(${userAccount})`,
  357. targetType: 1,
  358. targetName: teacherName,
  359. targetId: teacherId,
  360. studentCode: null,
  361. roleCode,
  362. roleName,
  363. gradeCode,
  364. gradeName,
  365. subjectCode: one.subjectCode,
  366. subjectName: one.subjectName,
  367. classIdCode,
  368. className,
  369. classType: null,
  370. classTypeName: null,
  371. departmentId: null,
  372. departmentName: null,
  373. departmentGroupId: null,
  374. departmentGroupName: null,
  375. disabled: !canDel,
  376. selected
  377. })
  378. })
  379. }
  380. })
  381. }
  382. })
  383. return arr
  384. }
  385. export const gradTreeData = (list) => {
  386. if (!list || list.length === 0) return []
  387. const arr = []
  388. list.forEach(one => {
  389. const { gradeName, gradeCode, classTypePersonVoList } = one
  390. const objectOne = {
  391. label: gradeName,
  392. id: gradeCode,
  393. children: []
  394. }
  395. arr.push(objectOne)
  396. if (classTypePersonVoList && classTypePersonVoList.length) {
  397. classTypePersonVoList.forEach(two => {
  398. const { classTypeName, classType, classInfoPersonVoList } = two
  399. const objectTwo = {
  400. label: classTypeName,
  401. id: `${gradeCode}_${classType}`,
  402. children: []
  403. }
  404. objectOne.children.push(objectTwo)
  405. if (classInfoPersonVoList && classInfoPersonVoList.length) {
  406. classInfoPersonVoList.forEach(three => {
  407. const { className, classIdCode, personVoList } = three
  408. const objectThree = {
  409. label: className,
  410. id: `${gradeCode}_${classType}_${classIdCode}`,
  411. children: []
  412. }
  413. objectTwo.children.push(objectThree)
  414. if (personVoList && personVoList.length) {
  415. personVoList.forEach((four, fourIndex) => {
  416. const {
  417. showName,
  418. teacherName,
  419. teacherId,
  420. userAccount,
  421. roleCode,
  422. roleName,
  423. courseCode,
  424. courseName,
  425. canDel,
  426. selected
  427. } = four
  428. objectThree.children.push({
  429. id: `${one.gradeCode}_${two.classType}_${three.classIdCode}_${teacherId}_${fourIndex}`,
  430. label: `${showName}(${userAccount})`,
  431. targetType: 1,
  432. targetName: teacherName,
  433. targetId: teacherId,
  434. studentCode: null,
  435. roleCode,
  436. roleName,
  437. gradeCode,
  438. gradeName,
  439. subjectCode: courseCode,
  440. subjectName: courseName,
  441. classIdCode: three.classIdCode,
  442. className: three.className,
  443. classType: two.classType,
  444. classTypeName: two.classTypeName,
  445. departmentId: null,
  446. departmentName: null,
  447. departmentGroupId: null,
  448. departmentGroupName: null,
  449. disabled: !canDel,
  450. selected
  451. })
  452. })
  453. }
  454. })
  455. }
  456. })
  457. }
  458. })
  459. return arr
  460. }
  461. export const permTreeData = (list) => {
  462. if (!list || list.length === 0) return []
  463. const arr = []
  464. list.forEach(one => {
  465. const { roleId, roleName, personVoList } = one
  466. const objectOne = {
  467. label: roleName,
  468. id: roleId,
  469. children: []
  470. }
  471. arr.push(objectOne)
  472. if (personVoList && personVoList.length) {
  473. personVoList.forEach((two, twoIndex) => {
  474. const {
  475. showName,
  476. teacherName,
  477. teacherId,
  478. userAccount,
  479. roleCode,
  480. roleName,
  481. gradeCode,
  482. gradeName,
  483. courseCode,
  484. courseName,
  485. canDel,
  486. selected
  487. } = two
  488. objectOne.children.push({
  489. id: `${one.roleId}_${teacherId}_${twoIndex}`,
  490. label: `${showName}(${userAccount})`,
  491. targetType: 1,
  492. targetName: teacherName,
  493. targetId: teacherId,
  494. studentCode: null,
  495. roleCode,
  496. roleName,
  497. gradeCode,
  498. gradeName,
  499. subjectCode: courseCode,
  500. subjectName: courseName,
  501. classIdCode: null,
  502. className: null,
  503. classType: null,
  504. classTypeName: null,
  505. departmentId: null,
  506. departmentName: null,
  507. departmentGroupId: null,
  508. departmentGroupName: null,
  509. disabled: !canDel,
  510. selected
  511. })
  512. })
  513. }
  514. })
  515. return arr
  516. }
  517. /**
  518. * 根据文件后缀获取文件类型
  519. * @param {string} fileType 文件后缀,如 "jpg"
  520. * @returns {'image'|'video'|'audio'|'document'|''}
  521. */
  522. export const getFileTypeBySuffix = (fileType) => {
  523. // 图片后缀
  524. const IMAGE_EXTS = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico', 'tiff'];
  525. // 视频后缀
  526. const VIDEO_EXTS = ['mp4', 'mov', 'avi', 'mkv', 'flv', 'wmv', 'rmvb', 'mpeg', 'mpg', 'webm'];
  527. // 音频后缀
  528. const AUDIO_EXTS = ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma'];
  529. // 文档后缀
  530. const DOC_EXTS = ['doc', 'docx'];
  531. const XLS_EXTS = ['xls', 'xlsx'];
  532. const PPT_EXTS = ['ppt', 'pptx'];
  533. const PDF_EXTS = ['pdf'];
  534. const TXT_EXTS = ['txt'];
  535. if (!fileType) return '';
  536. // 后缀,转小写
  537. const ext = fileType.toLowerCase();
  538. if(IMAGE_EXTS.includes(ext)){
  539. return 'image'
  540. }else if (VIDEO_EXTS.includes(ext)){
  541. return 'video'
  542. }else if (AUDIO_EXTS.includes(ext)){
  543. return 'audio'
  544. }else if (DOC_EXTS.includes(ext)){
  545. return 'word'
  546. }else if (XLS_EXTS.includes(ext)){
  547. return 'excel'
  548. }else if (PPT_EXTS.includes(ext)){
  549. return 'pptx'
  550. }else if (PDF_EXTS.includes(ext)){
  551. return 'pdf'
  552. }else if (TXT_EXTS.includes(ext)){
  553. return 'txt'
  554. }else{
  555. return ''
  556. }
  557. }