utils.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. import { message } from 'antd';
  2. import { RcFile } from 'antd/lib/upload';
  3. import { parse } from 'querystring';
  4. /* eslint no-useless-escape:0 import/prefer-default-export:0 */
  5. const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
  6. export const isUrl = (path: string): boolean => reg.test(path);
  7. export const isAntDesignPro = (): boolean => {
  8. if (ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
  9. return true;
  10. }
  11. return window.location.hostname === 'preview.pro.ant.design';
  12. };
  13. // 给官方演示站点用,用于关闭真实开发环境不需要使用的特性
  14. export const isAntDesignProOrDev = (): boolean => {
  15. const { NODE_ENV } = process.env;
  16. if (NODE_ENV === 'development') {
  17. return true;
  18. }
  19. return isAntDesignPro();
  20. };
  21. export const getPageQuery = () => {
  22. const { href } = window.location;
  23. const qsIndex = href.indexOf('?');
  24. const sharpIndex = href.indexOf('#');
  25. if (qsIndex !== -1) {
  26. if (qsIndex > sharpIndex) {
  27. return parse(href.split('?')[1]);
  28. }
  29. return parse(href.slice(qsIndex + 1, sharpIndex));
  30. }
  31. return {};
  32. };
  33. // 排序这是比较函数
  34. export const compare = (field: string, order: 'descend' | 'ascend') => {
  35. // descend 降序 大到小 ascend 升序 小到大
  36. if (order === 'ascend') {
  37. return function (m: any, n: any) {
  38. var a = m[field];
  39. var b = n[field];
  40. return a - b; //升序
  41. }
  42. } else {
  43. return function (m: any, n: any) {
  44. var a = m[field];
  45. var b = n[field];
  46. return b - a; //降序
  47. }
  48. }
  49. }
  50. // 返回别名
  51. export const getChannelName = (name: string) => {
  52. let newName = name
  53. let abridgeServer: string[] = ['知定', '巨网', '广联', '太古', '云广', '傲星', '弘捷', '开域']
  54. let asName = abridgeServer.find((item: string) => name?.indexOf(item) !== -1)
  55. if (asName) {
  56. newName = asName
  57. } else if (newName?.length > 5) {
  58. newName = newName?.slice(2, 5) + '...'
  59. }
  60. return newName
  61. }
  62. /**
  63. * 转正则
  64. * @param arr
  65. * @returns
  66. */
  67. function arrayToRegex(arr: string[]) {
  68. // 转义正则表达式中的特殊字符
  69. function escapeRegExp(str: string) {
  70. return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  71. }
  72. // 将数组中的每个元素用括号包裹,并用 | 连接
  73. const regexString = arr.map(item => `(${escapeRegExp(item)})`).join('|');
  74. // 创建正则表达式
  75. const regex = new RegExp(regexString, 'g');
  76. return regex;
  77. }
  78. const arr = ['[微笑]', '[撇嘴]', '[色]', '[发呆]', '[流泪]', '[害羞]', '[睡]', '[大哭]', '[尴尬]', '[发怒]', '[调皮]', '[呲牙]', '[惊讶]', '[难过]', '[冷汗]', '[抓狂]', '[偷笑]', '[愉快]', '[白眼]', '[傲慢]', '[惊恐]', '[流汗]', '[憨笑]', '[奋斗]', '[疑问]', '[晕]', '[衰]', '[敲打]', '[再见]', '[擦汗]', '[鼓掌]', '[坏笑]', '[左哼哼]', '[右哼哼]', '[哈欠]', '[委屈]', '[快哭了]', '[阴险]', '[亲亲]', '[可怜]', '[西瓜]', '[咖啡]', '[猪头]', '[玫瑰]', '[嘴唇]', '[爱心]', '[蛋糕]', '[月亮]', '[太阳]', '[拥抱]', '[强]', '[胜利]', '[握手]', '[抱拳]', '[勾引]', '[拳头]', '[OK]', '[跳跳]', '[发抖]', '[怄火]', '[转圈]', '[嘿哈]', '[捂脸]', '[奸笑]', '[机智]', '[皱眉]', '[耶]', '[加油]', '[汗]', '[天啊]', '[社会社会]', '[旺柴]', '[好的]', '[加油加油]', '[哇]', '[红包]', '[發]', '[福]'];
  79. export function extractAndFilterBracketsContent(input: string): { extracted: string[], filteredString: string } {
  80. const regex = arrayToRegex(arr);
  81. const matches: string[] = [];
  82. let match;
  83. // 提取方括号内的内容
  84. while ((match = regex.exec(input)) !== null) {
  85. matches.push(match[1]);
  86. }
  87. // 过滤掉原字符串中方括号及其包裹的内容
  88. const filteredString = input.replace(regex, '');
  89. return { extracted: matches, filteredString: filteredString };
  90. }
  91. // 输入文案时判断
  92. export const txtLength = (t?: string) => {
  93. if (t) {
  94. const result = extractAndFilterBracketsContent(t);
  95. let extracted = result.extracted;
  96. let value = result.filteredString;
  97. let length = value?.length
  98. let text = value?.replace(/[\x00-\xff]/g, '')
  99. return extracted.length + text?.length + Number(((length - text?.length) / 2).toFixed())
  100. } else {
  101. return 0
  102. }
  103. }
  104. // 返回图片宽高
  105. export const getImgSize = (file: RcFile): Promise<{ width: number, height: number }> => {
  106. return new Promise((resolve: (value: any) => void, reject: (reason?: any) => void) => {
  107. if (file) {
  108. let img: any = new Image();
  109. let _URL = window.URL || window.webkitURL;
  110. img.onload = function (e: any) {
  111. resolve({ width: this.width, height: this.height })
  112. }
  113. img.src = _URL.createObjectURL(file);
  114. } else {
  115. reject()
  116. }
  117. })
  118. }
  119. // 返回落地页组件key
  120. export const getTypeKey = (key: string): string => {
  121. switch (key) {
  122. case 'TOP_IMAGE':
  123. return 'topImageSpec'
  124. case 'TOP_SLIDER':
  125. return 'topSliderSpec'
  126. case 'TOP_VIDEO':
  127. return 'topVideoSpec'
  128. case 'IMAGE':
  129. return 'imageSpec'
  130. case 'TEXT':
  131. return 'textSpec'
  132. case 'GH':
  133. return 'ghSpec'
  134. case 'ENTERPRISE_WX':
  135. return 'enterpriseWxSpec'
  136. case 'IMAGE_TEXT':
  137. return 'imageTextSpec'
  138. case 'FLOAT_BUTTON':
  139. return 'floatButtonSpec'
  140. }
  141. return ''
  142. }
  143. // 点击复制
  144. export const copy = (str: string) => {
  145. let element = document.createElement("textarea");
  146. element.id = 'myTextarea'
  147. element.textContent = str
  148. document.body.append(element);
  149. (document.getElementById('myTextarea') as any).select();
  150. document.execCommand("Copy")
  151. document.body.removeChild(element);
  152. message.success(`复制成功:${str}`)
  153. }
  154. // 数组分组
  155. export const groupBy = (array: any[], f: (item: any) => any[], isObject?: boolean) => {
  156. const groups: any = {};
  157. array.forEach(function (o) { //注意这里必须是forEach 大写
  158. const group = JSON.stringify(f(o));
  159. groups[group] = groups[group] || [];
  160. groups[group].push(o);
  161. });
  162. if (isObject) {
  163. return groups
  164. }
  165. return Object.keys(groups).map(function (group) {
  166. return groups[group];
  167. });
  168. }
  169. export const replaceSpecialTxt = (text: string | number | null | undefined) => {
  170. if (text) {
  171. return text.toString().replace(/[<>]/ig, '')
  172. } else {
  173. return text
  174. }
  175. }
  176. /**
  177. * 随机生成字符串
  178. * @param flag
  179. * @param min
  180. * @param max
  181. * @returns string
  182. */
  183. export const randomString = (flag: boolean, min: number, max: number) => {
  184. let str = "", range = min
  185. let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
  186. if (flag) {
  187. range = Math.round(Math.random() * (max - min)) + min;
  188. }
  189. for (let i = 0; i < range; i++) {
  190. let pos = Math.round(Math.random() * (arr.length - 1));
  191. str += arr[pos];
  192. }
  193. return str;
  194. }
  195. /**
  196. * 找2个数组不同元素
  197. * @param arr1
  198. * @param arr2
  199. * @returns
  200. */
  201. export const getArrDifference = (arr1: any[], arr2: any[]) => {
  202. return arr1.concat(arr2).filter((v, i, arr) => {
  203. return arr.indexOf(v) === arr.lastIndexOf(v);
  204. });
  205. }
  206. /**
  207. * 拼接oss参数获取视频首针图
  208. * @param videoUrl t_0 0秒的视频截图 t_10000 10秒的视频截图帧
  209. */
  210. export const getVideoImgUrl = (videoUrl: string) => {
  211. if (['.mp4', '.swf', '.flv', '.rm', '.ram', '.mov', '.mpg', '.mpeg', '.wmv', '.avi'].some(item => videoUrl.includes(item))) {
  212. return videoUrl + "?x-oss-process=video/snapshot,t_0,f_jpg,w_0,h_0,m_fast,ar_auto"
  213. }
  214. return videoUrl
  215. }
  216. // 设置值
  217. export const setValueAtPath = (path: string, object: { [k: string]: any }, value: any): void => {
  218. if (path.startsWith('/')) {
  219. path = path.slice(1);
  220. }
  221. let parts = path.split('/');
  222. let current: { [k: string]: any } = object;
  223. for (let i = 0; i < parts.length; i++) {
  224. let part = parts[i];
  225. if (i === parts.length - 2) {
  226. let children = current?.['children'] || {}
  227. children[parts[parts.length - 1]] = value
  228. current['children'] = children;
  229. break
  230. } else {
  231. if (!(part in current)) {
  232. current[part] = {};
  233. }
  234. current = current[part];
  235. }
  236. }
  237. }
  238. /**
  239. * 处理创意数据
  240. * @param data
  241. * @returns
  242. */
  243. export const processData = (data: string | any[]) => {
  244. let children = [], parentData: { [k: string]: any } = {};
  245. for (let i = 0; i < data.length; i += 1) {
  246. let node = data[i];
  247. if (!(node?.parentName) || node?.parentName === "") {
  248. parentData[node.name] = {
  249. ...data[i],
  250. children: {}
  251. }
  252. } else {
  253. children.push(data[i])
  254. }
  255. }
  256. // console.log('children--->', children)
  257. let jumpInfo = [], list: any[] = [], left_button: any = {}, right_button: any = {}
  258. for (let i = 0; i < children.length; i += 1) {
  259. let node = children[i];
  260. if (node.parentName) {
  261. if (node.parentName === "jump_info" && !['/jump_info/page_type', '/jump_info/backups', '/jump_info/page_spec'].includes(node.path)) {
  262. jumpInfo.push(node)
  263. } else if (node.parentName === "list") {
  264. list.push(node)
  265. } else if (node.parentName === "left_button") {
  266. left_button[node.name] = node
  267. } else if (node.parentName === "right_button") {
  268. right_button[node.name] = node
  269. } else {
  270. let c = parentData[node.parentName]
  271. if (c) {
  272. c['children'][node.name] = node
  273. parentData[node.parentName] = c
  274. } else {
  275. console.log('空childen-->', node.parentName)
  276. }
  277. }
  278. } else {
  279. console.log('空node.parentName--->', node)
  280. }
  281. }
  282. if (jumpInfo.length > 0) {
  283. jumpInfo.forEach(item => {
  284. if (item.path) {
  285. setValueAtPath(item.path, parentData, item)
  286. } else {
  287. console.log('没有path---->', item)
  288. }
  289. })
  290. }
  291. if (Object.keys(left_button).length > 0) {
  292. let children = {
  293. text: left_button?.text
  294. }
  295. let newLeftButton = [{ ...left_button?.jump_info, children }]
  296. newLeftButton.forEach(item => {
  297. if (item.path) {
  298. setValueAtPath(item.path, parentData, item)
  299. } else {
  300. console.log('没有path---->', item)
  301. }
  302. })
  303. }
  304. if (Object.keys(right_button).length > 0) {
  305. let children = {
  306. text: right_button?.text
  307. }
  308. let newRightButton = [{ ...right_button?.jump_info, children }]
  309. newRightButton.forEach(item => {
  310. if (item.path) {
  311. setValueAtPath(item.path, parentData, item)
  312. } else {
  313. console.log('没有path---->', item)
  314. }
  315. })
  316. }
  317. if (list?.length > 0 && Object.keys(parentData).includes('label')) {
  318. let l = parentData?.label?.children?.list
  319. if (l) {
  320. let lChildren: any = {}
  321. list.forEach(item => {
  322. lChildren[item.name] = item
  323. })
  324. l = { ...l, children: lChildren }
  325. parentData.label.children.list = l
  326. }
  327. }
  328. if (list?.length > 0 && Object.keys(parentData).includes('element_story')) {
  329. let l = parentData?.element_story?.children?.list
  330. if (l) {
  331. let lChildren: any = {}
  332. list.forEach(item => {
  333. lChildren[item.name] = item
  334. })
  335. l = { ...l, children: lChildren }
  336. parentData.element_story.children.list = l
  337. }
  338. }
  339. if (list?.length > 0 && Object.keys(parentData).includes('image_list')) {
  340. let l = parentData?.image_list?.children?.list
  341. if (l) {
  342. let lChildren: any = {}
  343. list.forEach(item => {
  344. lChildren[item.name] = item
  345. })
  346. l = { ...l, children: lChildren }
  347. parentData.image_list.children.list = l
  348. }
  349. }
  350. return parentData;
  351. }
  352. /**
  353. * 数组叉乘
  354. * @param arr1
  355. * @param arr2
  356. * @returns
  357. */
  358. export function cartesianProduct<T, U>(arr1: T[], arr2: U[]): [T, U, string][] {
  359. return arr1.flatMap((d1, index1) => arr2.map((d2, index2) => [d1, d2, `${index1 + 1}_${index2 + 1}`] as [T, U, string]));
  360. }
  361. /**
  362. * 对比2个数组是否相同,不考虑下表
  363. * @param arr1
  364. * @param arr2
  365. * @returns
  366. */
  367. export const arraysHaveSameValues = (arr1: any[], arr2: any[]): boolean => {
  368. // 首先检查数组的长度是否相同
  369. if (arr1.length !== arr2.length) {
  370. return false;
  371. }
  372. // 使用数组的 sort 方法对两个数组进行排序,然后逐个比较
  373. const sortedArr1 = [...arr1].sort();
  374. const sortedArr2 = [...arr2].sort();
  375. for (let i = 0; i < sortedArr1.length; i++) {
  376. if (sortedArr1[i] !== sortedArr2[i]) {
  377. return false;
  378. }
  379. }
  380. return true;
  381. }
  382. /**
  383. * 定义一个函数,用于将数组平均分配给多个数组
  384. * @param originalArray
  385. * @param numGroups
  386. * @returns
  387. */
  388. export const distributeArray = (originalArray: any[], numGroups: number): any[][] => {
  389. // 初始化结果数组,每个子数组为空
  390. const result: any[][] = Array.from({ length: numGroups }, () => []);
  391. // 分配原始数组中的元素到结果数组
  392. originalArray.forEach((element, index) => {
  393. result[index % numGroups].push(element);
  394. });
  395. return result;
  396. };
  397. /**
  398. * 随机取几个值
  399. * @param arr
  400. * @param n
  401. * @returns
  402. */
  403. export function getRandomElements<T>(arr: T[], n: number): T[] {
  404. if (n > arr.length) {
  405. throw new RangeError("getRandomElements: more elements taken than available");
  406. }
  407. const result: T[] = new Array(n);
  408. let len: number = arr.length;
  409. const taken: number[] = new Array(len);
  410. while (n--) {
  411. const x: number = Math.floor(Math.random() * len);
  412. result[n] = arr[x in taken ? taken[x] : x];
  413. taken[x] = --len in taken ? taken[len] : len;
  414. }
  415. return result;
  416. }
  417. /**
  418. * 打乱数组
  419. * @param array
  420. * @returns
  421. */
  422. export function shuffleArray<T>(array: T[]): T[] {
  423. const shuffled = array.slice();
  424. for (let i = shuffled.length - 1; i > 0; i--) {
  425. const j = Math.floor(Math.random() * (i + 1));
  426. [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
  427. }
  428. return shuffled;
  429. }
  430. export function chunkArray<T>(array: T[], numChunks: number): T[][] {
  431. const chunks: T[][] = Array.from({ length: numChunks }, () => []);
  432. array.forEach((item, index) => {
  433. chunks[index % numChunks].push(item);
  434. });
  435. return chunks;
  436. }
  437. /**
  438. * 按多少个一组分组
  439. * @param array
  440. * @param chunkSize
  441. * @returns
  442. */
  443. export function chunkArray1<T>(array: T[], chunkSize: number): T[][] {
  444. const result: T[][] = [];
  445. for (let i = 0; i < array.length; i += chunkSize) {
  446. result.push(array.slice(i, i + chunkSize));
  447. }
  448. return result;
  449. }
  450. /**
  451. * 打乱随机分配数组
  452. * @param array
  453. * @param numChunks
  454. * @returns
  455. */
  456. export function splitArrayIntoRandomChunks<T>(array: T[], numChunks: number): T[][] {
  457. const shuffledArray = shuffleArray(array);
  458. return chunkArray(shuffledArray, numChunks);
  459. }
  460. /**
  461. * 秒数格式化
  462. * @param seconds 秒
  463. * @returns
  464. */
  465. export function formatSecondsToTime(seconds: number): string {
  466. const hours = Math.floor(seconds / 3600);
  467. const minutes = Math.floor((seconds % 3600) / 60);
  468. const secs = seconds % 60;
  469. // 使用 padStart 确保每个部分都是两位数
  470. return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
  471. }
  472. /**
  473. * 文件大小转换
  474. * @param bytes
  475. * @returns
  476. */
  477. export function formatBytes(bytes: number) {
  478. if (bytes < 1048576) { // 1024 * 1024
  479. return (bytes / 1024).toFixed(2) + ' KB';
  480. } else if (bytes < 1073741824) { // 1024 * 1024 * 1024
  481. return (bytes / 1048576).toFixed(2) + ' MB';
  482. } else {
  483. return (bytes / 1073741824).toFixed(2) + ' GB';
  484. }
  485. }
  486. /**
  487. * 下划线转驼峰
  488. * @param s
  489. * @returns
  490. */
  491. export const toCamelCase = (s: string): string => {
  492. return s.replace(/_([a-zA-Z])/g, (_, g1) => g1.toUpperCase());
  493. }