index.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
  2. import type { App, Plugin } from 'vue';
  3. import type { FormSchema } from '@/components/Form';
  4. import { unref } from 'vue';
  5. import { isObject, isFunction, isString } from '/@/utils/is';
  6. import Big from 'big.js';
  7. // update-begin--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
  8. export const URL_HASH_TAB = `__AGWE4H__HASH__TAG__PWHRG__`;
  9. // update-end--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
  10. export const noop = () => {};
  11. /**
  12. * @description: Set ui mount node
  13. */
  14. export function getPopupContainer(node?: HTMLElement): HTMLElement {
  15. return (node?.parentNode as HTMLElement) ?? document.body;
  16. }
  17. /**
  18. * Add the object as a parameter to the URL
  19. * @param baseUrl url
  20. * @param obj
  21. * @returns {string}
  22. * eg:
  23. * let obj = {a: '3', b: '4'}
  24. * setObjToUrlParams('www.baidu.com', obj)
  25. * ==>www.baidu.com?a=3&b=4
  26. */
  27. export function setObjToUrlParams(baseUrl: string, obj: any): string {
  28. let parameters = '';
  29. for (const key in obj) {
  30. parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
  31. }
  32. parameters = parameters.replace(/&$/, '');
  33. return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
  34. }
  35. export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
  36. let key: string;
  37. for (key in target) {
  38. // update-begin--author:liaozhiyang---date:20240329---for:【QQYUN-7872】online表单label较长优化
  39. if (isObject(src[key]) && isObject(target[key])) {
  40. src[key] = deepMerge(src[key], target[key]);
  41. } else {
  42. // update-begin--author:liaozhiyang---date:20250318---for:【issues/7940】componentProps写成函数形式时,updateSchema写成对象时,参数没合并
  43. try {
  44. if (isFunction(src[key]) && isObject(src[key]()) && isObject(target[key])) {
  45. // src[key]是函数且返回对象,且target[key]是对象
  46. src[key] = deepMerge(src[key](), target[key]);
  47. } else if (isObject(src[key]) && isFunction(target[key]) && isObject(target[key]())) {
  48. // target[key]是函数且返回对象,且src[key]是对象
  49. src[key] = deepMerge(src[key], target[key]());
  50. } else if (isFunction(src[key]) && isFunction(target[key]) && isObject(src[key]()) && isObject(target[key]())) {
  51. // src[key]是函数且返回对象,target[key]是函数且返回对象
  52. src[key] = deepMerge(src[key](), target[key]());
  53. } else {
  54. src[key] = target[key];
  55. }
  56. } catch (error) {
  57. src[key] = target[key];
  58. }
  59. // update-end--author:liaozhiyang---date:20250318---for:【issues/7940】componentProps写成函数形式时,updateSchema写成对象时,参数没合并
  60. }
  61. // update-end--author:liaozhiyang---date:20240329---for:【QQYUN-7872】online表单label较长优化
  62. }
  63. return src;
  64. }
  65. export function openWindow(url: string, opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean }) {
  66. const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
  67. const feature: string[] = [];
  68. noopener && feature.push('noopener=yes');
  69. noreferrer && feature.push('noreferrer=yes');
  70. window.open(url, target, feature.join(','));
  71. }
  72. // dynamic use hook props
  73. export function getDynamicProps<T, U>(props: T): Partial<U> {
  74. const ret: Recordable = {};
  75. // @ts-ignore
  76. Object.keys(props).map((key) => {
  77. ret[key] = unref((props as Recordable)[key]);
  78. });
  79. return ret as Partial<U>;
  80. }
  81. /**
  82. * 获取表单字段值数据类型
  83. * @param props
  84. * @param field
  85. * @updateBy:zyf
  86. */
  87. export function getValueType(props, field) {
  88. let formSchema = unref(unref(props)?.schemas);
  89. let valueType = 'string';
  90. if (formSchema) {
  91. let schema = formSchema.filter((item) => item.field === field)[0];
  92. valueType = schema && schema.componentProps && schema.componentProps.valueType ? schema.componentProps.valueType : valueType;
  93. }
  94. return valueType;
  95. }
  96. /**
  97. * 获取表单字段值数据类型
  98. * @param schema
  99. */
  100. export function getValueTypeBySchema(schema: FormSchema) {
  101. let valueType = 'string';
  102. if (schema) {
  103. const componentProps = schema.componentProps as Recordable;
  104. valueType = componentProps?.valueType ? componentProps?.valueType : valueType;
  105. }
  106. return valueType;
  107. }
  108. export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormalized {
  109. if (!route) return route;
  110. const { matched, ...opt } = route;
  111. return {
  112. ...opt,
  113. matched: (matched
  114. ? matched.map((item) => ({
  115. meta: item.meta,
  116. name: item.name,
  117. path: item.path,
  118. }))
  119. : undefined) as RouteRecordNormalized[],
  120. };
  121. }
  122. /**
  123. * 深度克隆对象、数组
  124. * @param obj 被克隆的对象
  125. * @return 克隆后的对象
  126. */
  127. export function cloneObject(obj) {
  128. return JSON.parse(JSON.stringify(obj));
  129. }
  130. export const withInstall = <T>(component: T, alias?: string) => {
  131. //console.log("---初始化---", component)
  132. const comp = component as any;
  133. comp.install = (app: App) => {
  134. // @ts-ignore
  135. app.component(comp.name || comp.displayName, component);
  136. if (alias) {
  137. app.config.globalProperties[alias] = component;
  138. }
  139. };
  140. return component as T & Plugin;
  141. };
  142. /**
  143. * 获取url地址参数
  144. * @param paraName
  145. */
  146. export function getUrlParam(paraName) {
  147. let url = document.location.toString();
  148. let arrObj = url.split('?');
  149. if (arrObj.length > 1) {
  150. let arrPara = arrObj[1].split('&');
  151. let arr;
  152. for (let i = 0; i < arrPara.length; i++) {
  153. arr = arrPara[i].split('=');
  154. if (arr != null && arr[0] == paraName) {
  155. return arr[1];
  156. }
  157. }
  158. return '';
  159. } else {
  160. return '';
  161. }
  162. }
  163. /**
  164. * 休眠(setTimeout的promise版)
  165. * @param ms 要休眠的时间,单位:毫秒
  166. * @param fn callback,可空
  167. * @return Promise
  168. */
  169. export function sleep(ms: number, fn?: Fn) {
  170. return new Promise<void>((resolve) =>
  171. setTimeout(() => {
  172. fn && fn();
  173. resolve();
  174. }, ms)
  175. );
  176. }
  177. /**
  178. * 不用正则的方式替换所有值
  179. * @param text 被替换的字符串
  180. * @param checker 替换前的内容
  181. * @param replacer 替换后的内容
  182. * @returns {String} 替换后的字符串
  183. */
  184. export function replaceAll(text, checker, replacer) {
  185. let lastText = text;
  186. text = text.replace(checker, replacer);
  187. if (lastText !== text) {
  188. return replaceAll(text, checker, replacer);
  189. }
  190. return text;
  191. }
  192. /**
  193. * 获取URL上参数
  194. * @param url
  195. */
  196. export function getQueryVariable(url) {
  197. if (!url) return;
  198. var t,
  199. n,
  200. r,
  201. i = url.split('?')[1],
  202. s = {};
  203. ((t = i.split('&')), (r = null), (n = null));
  204. for (var o in t) {
  205. var u = t[o].indexOf('=');
  206. u !== -1 && ((r = t[o].substr(0, u)), (n = t[o].substr(u + 1)), (s[r] = n));
  207. }
  208. return s;
  209. }
  210. /**
  211. * 判断是否显示办理按钮
  212. * @param bpmStatus
  213. * @returns {*}
  214. */
  215. export function showDealBtn(bpmStatus) {
  216. if (bpmStatus != '1' && bpmStatus != '3' && bpmStatus != '4') {
  217. return true;
  218. }
  219. return false;
  220. }
  221. /**
  222. * 数字转大写
  223. * @param value
  224. * @returns {*}
  225. */
  226. export function numToUpper(value) {
  227. if (value != '') {
  228. let unit = new Array('仟', '佰', '拾', '', '仟', '佰', '拾', '', '角', '分');
  229. const toDx = (n) => {
  230. switch (n) {
  231. case '0':
  232. return '零';
  233. case '1':
  234. return '壹';
  235. case '2':
  236. return '贰';
  237. case '3':
  238. return '叁';
  239. case '4':
  240. return '肆';
  241. case '5':
  242. return '伍';
  243. case '6':
  244. return '陆';
  245. case '7':
  246. return '柒';
  247. case '8':
  248. return '捌';
  249. case '9':
  250. return '玖';
  251. }
  252. };
  253. let lth = value.toString().length;
  254. // update-begin--author:liaozhiyang---date:20241202---for:【issues/7493】numToUpper方法返回解决错误
  255. value = new Big(value).times(100);
  256. // update-end--author:liaozhiyang---date:20241202---for:【issues/7493】numToUpper方法返回解决错误
  257. value += '';
  258. let length = value.length;
  259. if (lth <= 8) {
  260. let result = '';
  261. for (let i = 0; i < length; i++) {
  262. if (i == 2) {
  263. result = '元' + result;
  264. } else if (i == 6) {
  265. result = '万' + result;
  266. }
  267. if (value.charAt(length - i - 1) == 0) {
  268. if (i != 0 && i != 1) {
  269. if (result.charAt(0) != '零' && result.charAt(0) != '元' && result.charAt(0) != '万') {
  270. result = '零' + result;
  271. }
  272. }
  273. continue;
  274. }
  275. result = toDx(value.charAt(length - i - 1)) + unit[unit.length - i - 1] + result;
  276. }
  277. result += result.charAt(result.length - 1) == '元' ? '整' : '';
  278. return result;
  279. } else {
  280. return null;
  281. }
  282. }
  283. return null;
  284. }
  285. //update-begin-author:taoyan date:2022-6-8 for:解决老的vue2动态导入文件语法 vite不支持的问题
  286. const allModules = import.meta.glob('../views/**/*.vue');
  287. export function importViewsFile(path): Promise<any> {
  288. if (path.startsWith('/')) {
  289. path = path.substring(1);
  290. }
  291. let page = '';
  292. if (path.endsWith('.vue')) {
  293. page = `../views/${path}`;
  294. } else {
  295. page = `../views/${path}.vue`;
  296. }
  297. return new Promise((resolve, reject) => {
  298. let flag = true;
  299. for (const path in allModules) {
  300. if (path == page) {
  301. flag = false;
  302. allModules[path]().then((mod) => {
  303. console.log(path, mod);
  304. resolve(mod);
  305. });
  306. }
  307. }
  308. if (flag) {
  309. reject('该文件不存在:' + page);
  310. }
  311. });
  312. }
  313. //update-end-author:taoyan date:2022-6-8 for:解决老的vue2动态导入文件语法 vite不支持的问题
  314. /**
  315. * 跳转至积木报表的 预览页面
  316. * @param url
  317. * @param id
  318. * @param token
  319. */
  320. export function goJmReportViewPage(url, id, token) {
  321. // update-begin--author:liaozhiyang---date:20230904---for:【QQYUN-6390】eval替换成new Function,解决build警告
  322. // URL支持{{ window.xxx }}占位符变量
  323. url = url.replace(/{{([^}]+)?}}/g, (_s1, s2) => _eval(s2));
  324. // update-end--author:liaozhiyang---date:20230904---for:【QQYUN-6390】eval替换成new Function,解决build警告
  325. if (url.includes('?')) {
  326. url += '&';
  327. } else {
  328. url += '?';
  329. }
  330. url += `id=${id}`;
  331. url += `&token=${token}`;
  332. window.open(url);
  333. }
  334. /**
  335. * 获取随机颜色
  336. */
  337. export function getRandomColor(index?) {
  338. const colors = [
  339. 'rgb(100, 181, 246)',
  340. 'rgb(77, 182, 172)',
  341. 'rgb(255, 183, 77)',
  342. 'rgb(229, 115, 115)',
  343. 'rgb(149, 117, 205)',
  344. 'rgb(161, 136, 127)',
  345. 'rgb(144, 164, 174)',
  346. 'rgb(77, 208, 225)',
  347. 'rgb(129, 199, 132)',
  348. 'rgb(255, 138, 101)',
  349. 'rgb(133, 202, 205)',
  350. 'rgb(167, 214, 118)',
  351. 'rgb(254, 225, 89)',
  352. 'rgb(251, 199, 142)',
  353. 'rgb(239, 145, 139)',
  354. 'rgb(169, 181, 255)',
  355. 'rgb(231, 218, 202)',
  356. 'rgb(252, 128, 58)',
  357. 'rgb(254, 161, 172)',
  358. 'rgb(194, 163, 205)',
  359. ];
  360. return index && index < 19 ? colors[index] : colors[Math.floor(Math.random() * (colors.length - 1))];
  361. }
  362. export function getRefPromise(componentRef) {
  363. return new Promise((resolve) => {
  364. (function next() {
  365. const ref = componentRef.value;
  366. if (ref) {
  367. resolve(ref);
  368. } else {
  369. setTimeout(() => {
  370. next();
  371. }, 100);
  372. }
  373. })();
  374. });
  375. }
  376. /**
  377. * 2023-09-04
  378. * liaozhiyang
  379. * 用new Function替换eval
  380. */
  381. export function _eval(str: string) {
  382. return new Function(`return ${str}`)();
  383. }
  384. /**
  385. * 2024-04-30
  386. * liaozhiyang
  387. * 通过时间或者时间戳获取对应antd的年、月、周、季度。
  388. */
  389. export function getWeekMonthQuarterYear(date) {
  390. // 获取 ISO 周数的函数
  391. const getISOWeek = (date) => {
  392. const jan4 = new Date(date.getFullYear(), 0, 4);
  393. const oneDay = 86400000; // 一天的毫秒数
  394. return Math.ceil(((date - jan4.getTime()) / oneDay + jan4.getDay() + 1) / 7);
  395. };
  396. // 将时间戳转换为日期对象
  397. const dateObj = new Date(date);
  398. // 计算周
  399. const week = getISOWeek(dateObj);
  400. // 计算月
  401. const month = dateObj.getMonth() + 1; // 月份是从0开始的,所以要加1
  402. // 计算季度
  403. const quarter = Math.floor(dateObj.getMonth() / 3) + 1;
  404. // 计算年
  405. const year = dateObj.getFullYear();
  406. return {
  407. year: `${year}`,
  408. month: `${year}-${month.toString().padStart(2, '0')}`,
  409. week: `${year}-${week}周`,
  410. quarter: `${year}-Q${quarter}`,
  411. };
  412. }
  413. /**
  414. * 2024-05-17
  415. * liaozhiyang
  416. * 设置挂载的modal元素有可能会有多个,需要找到对应的。
  417. */
  418. export const setPopContainer = (node, selector) => {
  419. if (typeof selector === 'string') {
  420. const targetEles = Array.from(document.querySelectorAll(selector));
  421. if (targetEles.length > 1) {
  422. const retrospect = (node, elems) => {
  423. let ele = node.parentNode;
  424. while (ele) {
  425. const findParentNode = elems.find((item) => item === ele);
  426. if (findParentNode) {
  427. ele = null;
  428. return findParentNode;
  429. } else {
  430. ele = ele.parentNode;
  431. }
  432. }
  433. return null;
  434. };
  435. const elem = retrospect(node, targetEles);
  436. if (elem) {
  437. return elem;
  438. } else {
  439. return document.querySelector(selector);
  440. }
  441. } else {
  442. return document.querySelector(selector);
  443. }
  444. } else {
  445. return selector;
  446. }
  447. };
  448. /**
  449. * 2024-06-14
  450. * liaozhiyang
  451. * 根据控件显示条件
  452. * label、value通用,title、val给权限管理用的
  453. */
  454. export function useConditionFilter() {
  455. // 通用条件
  456. const commonConditionOptions = [
  457. { label: '为空', value: 'empty', val: 'EMPTY' },
  458. { label: '不为空', value: 'not_empty', val: 'NOT_EMPTY' },
  459. ];
  460. // 数值、日期
  461. const numberConditionOptions = [
  462. { label: '等于', value: 'eq', val: '=' },
  463. { label: '在...中', value: 'in', val: 'IN', title: '包含' },
  464. { label: '不等于', value: 'ne', val: '!=' },
  465. { label: '大于', value: 'gt', val: '>' },
  466. { label: '大于等于', value: 'ge', val: '>=' },
  467. { label: '小于', value: 'lt', val: '<' },
  468. { label: '小于等于', value: 'le', val: '<=' },
  469. ...commonConditionOptions,
  470. ];
  471. // 文本、密码、多行文本、富文本、markdown
  472. const inputConditionOptions = [
  473. { label: '等于', value: 'eq', val: '=' },
  474. { label: '模糊', value: 'like', val: 'LIKE' },
  475. { label: '以..开始', value: 'right_like', title: '右模糊', val: 'RIGHT_LIKE' },
  476. { label: '以..结尾', value: 'left_like', title: '左模糊', val: 'LEFT_LIKE' },
  477. { label: '在...中', value: 'in', val: 'IN', title: '包含' },
  478. { label: '不等于', value: 'ne', val: '!=' },
  479. ...commonConditionOptions,
  480. ];
  481. // 下拉、单选、多选、开关、用户、部门、关联记录、省市区、popup、popupDict、下拉多选、下拉搜索、分类字典、自定义树
  482. const selectConditionOptions = [
  483. { label: '等于', value: 'eq', val: '=' },
  484. { label: '在...中', value: 'in', val: 'IN', title: '包含' },
  485. { label: '不等于', value: 'ne', val: '!=' },
  486. ...commonConditionOptions,
  487. ];
  488. const def = [
  489. { label: '等于', value: 'eq', val: '=' },
  490. { label: '模糊', value: 'like', val: 'LIKE' },
  491. { label: '以..开始', value: 'right_like', title: '右模糊', val: 'RIGHT_LIKE' },
  492. { label: '以..结尾', value: 'left_like', title: '左模糊', val: 'LEFT_LIKE' },
  493. { label: '在...中', value: 'in', val: 'IN', title: '包含' },
  494. { label: '不等于', value: 'ne', val: '!=' },
  495. { label: '大于', value: 'gt', val: '>' },
  496. { label: '大于等于', value: 'ge', val: '>=' },
  497. { label: '小于', value: 'lt', val: '<' },
  498. { label: '小于等于', value: 'le', val: '<=' },
  499. ...commonConditionOptions,
  500. ];
  501. const filterCondition = (data) => {
  502. if (data.view == 'text' && data.fieldType == 'number') {
  503. data.view = 'number';
  504. }
  505. switch (data.view) {
  506. case 'file':
  507. case 'image':
  508. case 'password':
  509. return commonConditionOptions;
  510. case 'text':
  511. case 'textarea':
  512. case 'umeditor':
  513. case 'markdown':
  514. case 'pca':
  515. case 'popup':
  516. return inputConditionOptions;
  517. case 'list':
  518. case 'radio':
  519. case 'checkbox':
  520. case 'switch':
  521. case 'sel_user':
  522. case 'sel_depart':
  523. case 'link_table':
  524. case 'popup_dict':
  525. case 'list_multi':
  526. case 'sel_search':
  527. case 'cat_tree':
  528. case 'sel_tree':
  529. return selectConditionOptions;
  530. case 'date':
  531. // number是虚拟的
  532. case 'number':
  533. return numberConditionOptions;
  534. default:
  535. return def;
  536. }
  537. };
  538. return { filterCondition };
  539. }
  540. // 获取url中的参数
  541. export const getUrlParams = (url) => {
  542. const result = {
  543. url: '',
  544. params: {},
  545. };
  546. const list = url.split('?');
  547. result.url = list[0];
  548. const params = list[1];
  549. if (params) {
  550. const list = params.split('&');
  551. list.forEach((ele) => {
  552. const dic = ele.split('=');
  553. const label = dic[0];
  554. result.params[label] = dic[1];
  555. });
  556. }
  557. return result;
  558. };
  559. /* 20250325
  560. * liaozhiyang
  561. * 分割url字符成数组
  562. * 【issues/7990】图片参数中包含逗号会错误的识别成多张图
  563. * */
  564. export const split = (str) => {
  565. if (isString(str)) {
  566. const text = str.trim();
  567. if (text.startsWith('http')) {
  568. const parts = str.split(',');
  569. const urls: any = [];
  570. let currentUrl = '';
  571. for (const part of parts) {
  572. if (part.startsWith('http://') || part.startsWith('https://')) {
  573. // 如果遇到新的URL开头,保存当前URL并开始新的URL
  574. if (currentUrl) {
  575. urls.push(currentUrl);
  576. }
  577. currentUrl = part;
  578. } else {
  579. // 否则,是当前URL的一部分(如参数)
  580. currentUrl += ',' + part;
  581. }
  582. }
  583. // 添加最后一个URL
  584. if (currentUrl) {
  585. urls.push(currentUrl);
  586. }
  587. return urls;
  588. } else {
  589. return str.split(',');
  590. }
  591. }
  592. return str;
  593. };
  594. /**
  595. * 判断一个对象的所有字段是否有值(非 null 和 undefined)
  596. * @param obj 要检查的对象
  597. * @returns 所有字段有值返回 true,否则 false
  598. */
  599. function areAllFieldsFilled(obj: Record<string, any>): boolean {
  600. return Object.values(obj).every((value) => unref(value) !== null && unref(value) !== undefined);
  601. }
  602. /**
  603. * 判断数组中每个对象是否所有字段都有值
  604. * @param arr 要检查的对象数组
  605. * @returns 所有对象字段都有值返回 true,否则 false
  606. */
  607. export function areAllItemsAllFieldsFilled(arr: Array<Record<string, any>>): boolean {
  608. // return arr.every((item) => areAllFieldsFilled(item));
  609. //修复新增的时候id校验
  610. return arr.every((item) => {
  611. const { id, ...rest } = item;
  612. return areAllFieldsFilled(rest);
  613. });
  614. }
  615. /**
  616. * ref对象转普通对象
  617. * @param obj
  618. * @returns
  619. */
  620. export const extractRefs = (obj) => {
  621. const result = {};
  622. Object.keys(obj).forEach((key) => {
  623. result[key] = unref(obj[key]);
  624. });
  625. return result;
  626. };
  627. /**
  628. * 将键值对数组转换为JSON对象
  629. * @param arr - 包含key和value属性的对象数组
  630. * @returns 转换后的JSON对象
  631. */
  632. export function convertArrayToJson(arr: { key: string; value: string }[]): Record<string, string> {
  633. const result: Record<string, string> = {};
  634. arr.forEach((item) => {
  635. result[item.key] = item.value;
  636. });
  637. return result;
  638. }
  639. /**
  640. * 将JSON对象转换为键值对数组
  641. * @param obj - JSON对象
  642. * @returns 包含key和value属性的对象数组
  643. */
  644. export function convertJsonToArray(obj: Record<string, any>): { key: string; value: string }[] {
  645. return Object.keys(obj).map((key) => ({
  646. key,
  647. value: obj[key].toString(),
  648. }));
  649. }