index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. import axios from 'axios'
  2. /**
  3. * 数据补全方法:将数据b的value值合并到数据a的对应key中
  4. * @param {Array} dataA 标准数据(需补全的原始数据)
  5. * @param {Array} dataB 待合并数据(提供补全值的数据)
  6. * @returns {Array} 补全后的数据
  7. */
  8. export function mergeData(dataA, dataB) {
  9. // 深拷贝原始数据避免污染‌:ml-citation{ref="1" data="citationList"}
  10. const mergedData = JSON.parse(JSON.stringify(dataA))
  11. // 遍历数据a,根据key匹配数据b并补全value‌:ml-citation{ref="4" data="citationList"}
  12. mergedData.forEach(itemA => {
  13. const matchedItemB = dataB.find(itemB => itemB.key === itemA.key)
  14. if (matchedItemB) {
  15. itemA.value = matchedItemB.value // 合并value
  16. itemA.alt = matchedItemB.alt || itemA.alt // 可选:合并其他字段如alt
  17. }
  18. })
  19. return mergedData
  20. }
  21. /**
  22. * Created by PanJiaChen on 16/11/18.
  23. */
  24. /**
  25. * Parse the time to string
  26. * @param {(Object|string|number)} time
  27. * @param {string} cFormat
  28. * @returns {string | null}
  29. */
  30. export function parseTime(time, cFormat) {
  31. if (arguments.length === 0 || !time) {
  32. return null
  33. }
  34. const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  35. let date
  36. if (typeof time === 'object') {
  37. date = time
  38. } else {
  39. if ((typeof time === 'string')) {
  40. if ((/^[0-9]+$/.test(time))) {
  41. // support "1548221490638"
  42. time = parseInt(time)
  43. } else {
  44. // support safari
  45. // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
  46. time = time.replace(new RegExp(/-/gm), '/')
  47. }
  48. }
  49. if ((typeof time === 'number') && (time.toString().length === 10)) {
  50. time = time * 1000
  51. }
  52. date = new Date(time)
  53. }
  54. const formatObj = {
  55. y: date.getFullYear(),
  56. m: date.getMonth() + 1,
  57. d: date.getDate(),
  58. h: date.getHours(),
  59. i: date.getMinutes(),
  60. s: date.getSeconds(),
  61. a: date.getDay()
  62. }
  63. const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
  64. const value = formatObj[key]
  65. // Note: getDay() returns 0 on Sunday
  66. if (key === 'a') {
  67. return ['日', '一', '二', '三', '四', '五', '六'][value]
  68. }
  69. return value.toString().padStart(2, '0')
  70. })
  71. return time_str
  72. }
  73. /**
  74. * @param {number} time
  75. * @param {string} option
  76. * @returns {string}
  77. */
  78. export function formatTime(time, option) {
  79. if (('' + time).length === 10) {
  80. time = parseInt(time) * 1000
  81. } else {
  82. time = +time
  83. }
  84. const d = new Date(time)
  85. const now = Date.now()
  86. const diff = (now - d) / 1000
  87. if (diff < 30) {
  88. return '刚刚'
  89. } else if (diff < 3600) {
  90. // less 1 hour
  91. return Math.ceil(diff / 60) + '分钟前'
  92. } else if (diff < 3600 * 24) {
  93. return Math.ceil(diff / 3600) + '小时前'
  94. } else if (diff < 3600 * 24 * 2) {
  95. return '1天前'
  96. }
  97. if (option) {
  98. return parseTime(time, option)
  99. } else {
  100. return (
  101. d.getMonth() +
  102. 1 +
  103. '月' +
  104. d.getDate() +
  105. '日' +
  106. d.getHours() +
  107. '时' +
  108. d.getMinutes() +
  109. '分'
  110. )
  111. }
  112. }
  113. /**
  114. * @param {string} url
  115. * @returns {Object}
  116. */
  117. export function getQueryObject(url) {
  118. url = url == null ? window.location.href : url
  119. const search = url.substring(url.lastIndexOf('?') + 1)
  120. const obj = {}
  121. const reg = /([^?&=]+)=([^?&=]*)/g
  122. search.replace(reg, (rs, $1, $2) => {
  123. const name = decodeURIComponent($1)
  124. let val = decodeURIComponent($2)
  125. val = String(val)
  126. obj[name] = val
  127. return rs
  128. })
  129. return obj
  130. }
  131. /**
  132. * @param {string} input value
  133. * @returns {number} output value
  134. */
  135. export function byteLength(str) {
  136. // returns the byte length of an utf8 string
  137. let s = str.length
  138. for (var i = str.length - 1; i >= 0; i--) {
  139. const code = str.charCodeAt(i)
  140. if (code > 0x7f && code <= 0x7ff) {
  141. s++
  142. } else if (code > 0x7ff && code <= 0xffff) s += 2
  143. if (code >= 0xDC00 && code <= 0xDFFF) i--
  144. }
  145. return s
  146. }
  147. /**
  148. * @param {Array} actual
  149. * @returns {Array}
  150. */
  151. export function cleanArray(actual) {
  152. const newArray = []
  153. for (let i = 0; i < actual.length; i++) {
  154. if (actual[i]) {
  155. newArray.push(actual[i])
  156. }
  157. }
  158. return newArray
  159. }
  160. /**
  161. * @param {Object} json
  162. * @returns {Array}
  163. */
  164. export function param(json) {
  165. if (!json) return ''
  166. return cleanArray(
  167. Object.keys(json).map(key => {
  168. if (json[key] === undefined) return ''
  169. return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
  170. })
  171. ).join('&')
  172. }
  173. /**
  174. * @param {string} url
  175. * @returns {Object}
  176. */
  177. export function param2Obj(url) {
  178. const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
  179. if (!search) {
  180. return {}
  181. }
  182. const obj = {}
  183. const searchArr = search.split('&')
  184. searchArr.forEach(v => {
  185. const index = v.indexOf('=')
  186. if (index !== -1) {
  187. const name = v.substring(0, index)
  188. const val = v.substring(index + 1, v.length)
  189. obj[name] = val
  190. }
  191. })
  192. return obj
  193. }
  194. /**
  195. * @param {string} val
  196. * @returns {string}
  197. */
  198. export function html2Text(val) {
  199. const div = document.createElement('div')
  200. div.innerHTML = val
  201. return div.textContent || div.innerText
  202. }
  203. /**
  204. * Merges two objects, giving the last one precedence
  205. * @param {Object} target
  206. * @param {(Object|Array)} source
  207. * @returns {Object}
  208. */
  209. export function objectMerge(target, source) {
  210. if (typeof target !== 'object') {
  211. target = {}
  212. }
  213. if (Array.isArray(source)) {
  214. return source.slice()
  215. }
  216. Object.keys(source).forEach(property => {
  217. const sourceProperty = source[property]
  218. if (typeof sourceProperty === 'object') {
  219. target[property] = objectMerge(target[property], sourceProperty)
  220. } else {
  221. target[property] = sourceProperty
  222. }
  223. })
  224. return target
  225. }
  226. /**
  227. * @param {HTMLElement} element
  228. * @param {string} className
  229. */
  230. export function toggleClass(element, className) {
  231. if (!element || !className) {
  232. return
  233. }
  234. let classString = element.className
  235. const nameIndex = classString.indexOf(className)
  236. if (nameIndex === -1) {
  237. classString += '' + className
  238. } else {
  239. classString =
  240. classString.substr(0, nameIndex) +
  241. classString.substr(nameIndex + className.length)
  242. }
  243. element.className = classString
  244. }
  245. /**
  246. * @param {string} type
  247. * @returns {Date}
  248. */
  249. export function getTime(type) {
  250. if (type === 'start') {
  251. return new Date().getTime() - 3600 * 1000 * 24 * 90
  252. } else {
  253. return new Date(new Date().toDateString())
  254. }
  255. }
  256. /**
  257. * @param {Function} func
  258. * @param {number} wait
  259. * @param {boolean} immediate
  260. * @return {*}
  261. */
  262. export function debounce(func, wait, immediate) {
  263. let timeout, args, context, timestamp, result
  264. const later = function() {
  265. // 据上一次触发时间间隔
  266. const last = +new Date() - timestamp
  267. // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
  268. if (last < wait && last > 0) {
  269. timeout = setTimeout(later, wait - last)
  270. } else {
  271. timeout = null
  272. // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
  273. if (!immediate) {
  274. result = func.apply(context, args)
  275. if (!timeout) context = args = null
  276. }
  277. }
  278. }
  279. return function(...args) {
  280. context = this
  281. timestamp = +new Date()
  282. const callNow = immediate && !timeout
  283. // 如果延时不存在,重新设定延时
  284. if (!timeout) timeout = setTimeout(later, wait)
  285. if (callNow) {
  286. result = func.apply(context, args)
  287. context = args = null
  288. }
  289. return result
  290. }
  291. }
  292. /**
  293. * This is just a simple version of deep copy
  294. * Has a lot of edge cases bug
  295. * If you want to use a perfect deep copy, use lodash's _.cloneDeep
  296. * @param {Object} source
  297. * @returns {Object}
  298. */
  299. export function deepClone(source) {
  300. if (!source && typeof source !== 'object') {
  301. throw new Error('error arguments', 'deepClone')
  302. }
  303. const targetObj = source.constructor === Array ? [] : {}
  304. Object.keys(source).forEach(keys => {
  305. if (source[keys] && typeof source[keys] === 'object') {
  306. targetObj[keys] = deepClone(source[keys])
  307. } else {
  308. targetObj[keys] = source[keys]
  309. }
  310. })
  311. return targetObj
  312. }
  313. /**
  314. * @param {Array} arr
  315. * @returns {Array}
  316. */
  317. export function uniqueArr(arr) {
  318. return Array.from(new Set(arr))
  319. }
  320. /**
  321. * @returns {string}
  322. */
  323. export function createUniqueString() {
  324. const timestamp = +new Date() + ''
  325. const randomNum = parseInt((1 + Math.random()) * 65536) + ''
  326. return (+(randomNum + timestamp)).toString(32)
  327. }
  328. /**
  329. * Check if an element has a class
  330. * @param {HTMLElement} elm
  331. * @param {string} cls
  332. * @returns {boolean}
  333. */
  334. export function hasClass(ele, cls) {
  335. return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
  336. }
  337. /**
  338. * Add class to element
  339. * @param {HTMLElement} elm
  340. * @param {string} cls
  341. */
  342. export function addClass(ele, cls) {
  343. if (!hasClass(ele, cls)) ele.className += ' ' + cls
  344. }
  345. /**
  346. * Remove class from element
  347. * @param {HTMLElement} elm
  348. * @param {string} cls
  349. */
  350. export function removeClass(ele, cls) {
  351. if (hasClass(ele, cls)) {
  352. const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
  353. ele.className = ele.className.replace(reg, ' ')
  354. }
  355. }
  356. /**
  357. * 引入dateFormat的处理
  358. */
  359. export function dateFormat(time, format) {
  360. format = format || 'Y-m-d H:i'
  361. var data = new Date(time)
  362. var o = {
  363. 'm+': prefixInteger(data.getMonth() + 1, 2), // 月份
  364. 'd+': prefixInteger(data.getDate(), 2), // 日
  365. 'H+': prefixInteger(data.getHours(), 2), // 小时
  366. 'i+': prefixInteger(data.getMinutes(), 2), // 分
  367. 's+': prefixInteger(data.getSeconds(), 2), // 秒
  368. 'q+': Math.floor((data.getMonth() + 3) / 3), // 季度
  369. 'S': prefixInteger(data.getMilliseconds(), 3) // 毫秒
  370. }
  371. if (/(Y+)/.test(format)) {
  372. format = format.replace(RegExp.$1, (data.getFullYear() + ''))
  373. }
  374. for (var k in o) {
  375. if (new RegExp('(' + k + ')').test(format)) {
  376. format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
  377. }
  378. }
  379. return format
  380. }
  381. /**
  382. * 自动补零
  383. * @param num
  384. * @param length
  385. * @returns {string}
  386. * @constructor
  387. */
  388. function prefixInteger(num, length) {
  389. return (Array(length).join('0') + num).slice(-length)
  390. }
  391. /**
  392. * 获取语言
  393. * @returns string
  394. */
  395. export function getLanguage() {
  396. const defLanguage = 'zh-cn'
  397. let language = localStorage.getItem('trtc-tuiPusher-language') || getUrlParam('lang') || defLanguage
  398. language = language.replace(/_/, '-').toLowerCase()
  399. if (language === 'zh-cn' || language === 'zh') {
  400. language = 'zh-cn'
  401. } else if (language === 'en' || language === 'en-us' || language === 'en-gb') {
  402. language = 'en-us'
  403. }
  404. return language
  405. }
  406. /**
  407. * 从 window.location.href 中获取指定key的value
  408. * @param {*} key 要获取的 key
  409. * @returns window.location.href 中指定key对应的value
  410. * @example
  411. * const value = getUrlParam(key);
  412. */
  413. export function getUrlParam(key) {
  414. const url = window.location.href.replace(/^[^?]*\?/, '')
  415. const regexp = new RegExp(`(^|&)${key}=([^&#]*)(&|$|)`, 'i')
  416. const paramMatch = url.match(regexp)
  417. return paramMatch ? paramMatch[2] : null
  418. }
  419. export function copyValue(val, callback) {
  420. const input = document.createElement('input')
  421. input.setAttribute('readonly', 'readonly') // 设置为只读, 防止在 ios 下拉起键盘
  422. // input.setAttribute('value', value); // textarea 不能用此方式赋值, 否则无法复制内容
  423. input.value = val
  424. document.body.appendChild(input)
  425. input.setSelectionRange(0, 9999) // 防止 ios 下没有全选内容而无法复制
  426. input.select()
  427. document.execCommand('copy')
  428. document.body.removeChild(input)
  429. if (callback) {
  430. callback()
  431. }
  432. }
  433. /**
  434. * 返回不合法的head内的标签
  435. * @param text
  436. * @returns {string}
  437. */
  438. export function getInvalidHeadTags(text) {
  439. const validTags = new RegExp('title|meta|base|style|script|noscript|link', 'i')
  440. const pattern = new RegExp('<[^>]+>', 'ig')
  441. const complain = new RegExp('<!--[^>]*>', 'ig')
  442. let tagArr = []
  443. let boolValidTag = -1
  444. let boolComplain = -1
  445. if (text) {
  446. tagArr = text.match(pattern)
  447. if (!tagArr) {
  448. return text
  449. }
  450. if (tagArr.length > 0) {
  451. const newArr = []
  452. for (const i in tagArr) {
  453. boolValidTag = tagArr[i].search(validTags)
  454. boolComplain = tagArr[i].search(complain)
  455. if (boolValidTag === -1 && boolComplain === -1) {
  456. newArr.push(tagArr[i])
  457. }
  458. }
  459. tagArr = newArr
  460. }
  461. }
  462. return tagArr.join('')
  463. }
  464. export function uploadFileData(input, callback) {
  465. if (input) {
  466. if (!input.hasAttribute('data-change-listener')) {
  467. input.setAttribute('data-change-listener', 'true')
  468. input.addEventListener('change', (e) => {
  469. let file = ''
  470. if (e.target.files && e.target.files[0]) {
  471. file = e.target.files[0]
  472. }
  473. const max_size = 1048576 * 20
  474. if (file.size > max_size) {
  475. return false
  476. }
  477. if (callback) {
  478. callback(file)
  479. }
  480. })
  481. }
  482. }
  483. }
  484. export async function getAiToken(callback) {
  485. await axios.post('https://apiv2-cn.matchexpo.cn/admin/admin-login', {
  486. password: 'Yj1132192460!',
  487. user_name: 'yanjian1'
  488. }).then(res => {
  489. if (res.data.token) {
  490. if (callback) {
  491. callback(res.data.token)
  492. }
  493. return res.data.token
  494. }
  495. })
  496. }
  497. export async function translateText(content, translate_lang, token, callback) {
  498. if (typeof content === 'string' && !(content.replace(/\s/g, ''))) {
  499. return ''
  500. }
  501. await axios.post('https://apiv2-cn.matchexpo.cn/admin/translate/content', {
  502. 'content': content,
  503. 'language': [translate_lang],
  504. 'prompte': '请将内容翻译为' + translate_lang
  505. }, {
  506. headers: {
  507. token: token
  508. }
  509. }).then(async res => {
  510. if (typeof content === 'string') {
  511. let text = res.data[translate_lang].replace('[', '')
  512. text = text.replace(']', '')
  513. await callback(text)
  514. } else {
  515. await callback(res.data[translate_lang])
  516. }
  517. })
  518. }
  519. export async function translateContent(content, item, translate_lang, token) {
  520. const parser = new DOMParser()
  521. const doc = parser.parseFromString(content, 'text/html')
  522. // 解析为html节点
  523. await traverse(doc.body)
  524. item.value = doc.body.innerHTML
  525. async function traverse(node) {
  526. if (node.nodeType === Node.TEXT_NODE) {
  527. const originalText = node.textContent
  528. // originalText 为文本节点内容,下面执行你的方法
  529. await translateText(originalText, translate_lang, token, (text) => {
  530. node.textContent = text
  531. })
  532. } else {
  533. for (const child of node.childNodes) {
  534. await traverse(child)
  535. }
  536. }
  537. }
  538. }