index.ts 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413
  1. import { isArray, isNullOrUnDef, isNumber, isString, isDate, isObject } from "@/utils/is";
  2. import { FieldNamesProps } from "@/components/LjVxeTable/interface";
  3. import XEUtils from "xe-utils";
  4. import { cloneDeep, pick, get } from "lodash-es";
  5. import dayjs from "dayjs";
  6. import { useAuthButtons } from "@/hooks/useAuthButtons";
  7. import { useUserStore } from "@/stores/modules/user";
  8. import { GetFormulaCompute } from "@/api/modules/common";
  9. import { ElMessage } from "element-plus";
  10. import type { Menu } from "@/typings/global";
  11. /**
  12. * @description 获取localStorage
  13. * @param {String} key Storage名称
  14. * @returns {String}
  15. */
  16. export function localGet(key: string) {
  17. const value = window.localStorage.getItem(key);
  18. try {
  19. return JSON.parse(window.localStorage.getItem(key) as string);
  20. } catch (error) {
  21. return value;
  22. }
  23. }
  24. /**
  25. * @description 存储localStorage
  26. * @param {String} key Storage名称
  27. * @param {*} value Storage值
  28. * @returns {void}
  29. */
  30. export function localSet(key: string, value: any) {
  31. window.localStorage.setItem(key, JSON.stringify(value));
  32. }
  33. /**
  34. * @description 清除localStorage
  35. * @param {String} key Storage名称
  36. * @returns {void}
  37. */
  38. export function localRemove(key: string) {
  39. window.localStorage.removeItem(key);
  40. }
  41. /**
  42. * @description 清除所有localStorage
  43. * @returns {void}
  44. */
  45. export function localClear() {
  46. window.localStorage.clear();
  47. }
  48. /**
  49. * @description 判断数据类型
  50. * @param {*} val 需要判断类型的数据
  51. * @returns {String}
  52. */
  53. export function isType(val: any) {
  54. if (val === null) return "null";
  55. if (typeof val !== "object") return typeof val;
  56. else return Object.prototype.toString.call(val).slice(8, -1).toLocaleLowerCase();
  57. }
  58. /**
  59. * @description 生成唯一 uuid
  60. * @returns {String}
  61. */
  62. export function generateUUID() {
  63. let uuid = "";
  64. for (let i = 0; i < 32; i++) {
  65. let random = (Math.random() * 16) | 0;
  66. if (i === 8 || i === 12 || i === 16 || i === 20) uuid += "-";
  67. uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16);
  68. }
  69. return uuid;
  70. }
  71. /**
  72. * 判断两个对象是否相同
  73. * @param {Object} a 要比较的对象一
  74. * @param {Object} b 要比较的对象二
  75. * @returns {Boolean} 相同返回 true,反之 false
  76. */
  77. export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) {
  78. if (!a || !b) return false;
  79. let aProps = Object.getOwnPropertyNames(a);
  80. let bProps = Object.getOwnPropertyNames(b);
  81. if (aProps.length != bProps.length) return false;
  82. for (let i = 0; i < aProps.length; i++) {
  83. let propName = aProps[i];
  84. let propA = a[propName];
  85. let propB = b[propName];
  86. if (!b.hasOwnProperty(propName)) return false;
  87. if (propA instanceof Object) {
  88. if (!isObjectValueEqual(propA, propB)) return false;
  89. } else if (propA !== propB) {
  90. return false;
  91. }
  92. }
  93. return true;
  94. }
  95. /**
  96. * @description 生成随机数
  97. * @param {Number} min 最小值
  98. * @param {Number} max 最大值
  99. * @returns {Number}
  100. */
  101. export function randomNum(min: number, max: number): number {
  102. let num = Math.floor(Math.random() * (min - max) + max);
  103. return num;
  104. }
  105. /**
  106. * @description 获取当前时间对应的提示语
  107. * @returns {String}
  108. */
  109. export function getTimeState(t: any) {
  110. // @ts-ignore
  111. let timeNow = new Date();
  112. let hours = timeNow.getHours();
  113. if (hours >= 6 && hours <= 10) return t("sys.greetings.morning") + ` ⛅`;
  114. if (hours >= 10 && hours <= 13) return t("sys.greetings.noon") + ` 🌞`;
  115. if (hours >= 13 && hours <= 18) return t("sys.greetings.afternoon") + ` 🌞`;
  116. if (hours >= 18 && hours <= 24) return t("sys.greetings.evening") + ` 🌛`;
  117. if (hours >= 0 && hours <= 6) return t("sys.greetings.deadOfNight") + ` 🌑`;
  118. }
  119. /**
  120. * @description 获取浏览器默认语言
  121. * @returns {String}
  122. */
  123. export function getBrowserLang() {
  124. let browserLang = navigator.language ? navigator.language : navigator.browserLanguage;
  125. let localLang = localGet("longjoe-global")?.language || "";
  126. localLang && (browserLang = localLang);
  127. console.log("getBrowserLang browserLang :>> ", browserLang, navigator.language, navigator.browserLanguage);
  128. let defaultBrowserLang = "";
  129. if (["cn", "zh", "zh-cn"].includes(browserLang.toLowerCase())) {
  130. defaultBrowserLang = "zh-cn";
  131. } else {
  132. defaultBrowserLang = "en";
  133. }
  134. return defaultBrowserLang;
  135. }
  136. /**
  137. * @description 使用递归扁平化菜单,方便添加动态路由
  138. * @param {Array} menuList 菜单列表
  139. * @returns {Array}
  140. */
  141. export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[] {
  142. let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList));
  143. // let newMenuList: Menu.MenuOptions[] = menuList;
  144. return newMenuList.flatMap(item => [item, ...(item.children ? getFlatMenuList(item.children) : [])]);
  145. }
  146. /**
  147. * @description 使用递归过滤出需要渲染在左侧菜单的列表 (需剔除 isHide == true 的菜单)
  148. * @param {Array} menuList 菜单列表
  149. * @returns {Array}
  150. * */
  151. export function getShowMenuList(menuList: Menu.MenuOptions[]) {
  152. const { CheckPower } = useAuthButtons(undefined);
  153. let newMenuList: Menu.MenuOptions[] = cloneDeep(menuList);
  154. return newMenuList.filter(item => {
  155. if (item.meta.funid && !CheckPower(item.meta.funid)) {
  156. return false;
  157. }
  158. // 父级节点隐藏,跳过,不需要再递归子级节点
  159. !item.meta?.isHide && item.children?.length && (item.children = getShowMenuList(item.children));
  160. // 过滤隐藏节点
  161. return !item.meta?.isHide;
  162. });
  163. }
  164. /**
  165. * @description 使用递归找出所有面包屑存储到 pinia/vuex 中
  166. * @param {Array} menuList 菜单列表
  167. * @param {Array} parent 父级菜单
  168. * @param {Object} result 处理后的结果
  169. * @returns {Object}
  170. */
  171. export const getAllBreadcrumbList = (menuList: Menu.MenuOptions[], parent = [], result: { [key: string]: any } = {}) => {
  172. for (const item of menuList) {
  173. result[item.path] = [...parent, item];
  174. if (item.children) getAllBreadcrumbList(item.children, result[item.path], result);
  175. }
  176. return result;
  177. };
  178. /**
  179. * @description 使用递归过滤出需要渲染在首页常用功能树 (需剔除 isHide == true 的菜单)
  180. * @param {Array} menuList 菜单列表
  181. * @returns {Array}
  182. * */
  183. export function getQuickEnterFuncTree(menuList: Menu.MenuOptions[]): any[] {
  184. class TreeNode {
  185. key: string;
  186. label: string;
  187. children: TreeNode[] | null;
  188. icon?: string;
  189. path?: string;
  190. disabled: boolean;
  191. constructor(key: string, label: string, hasChild?: boolean) {
  192. this.key = key;
  193. this.label = label;
  194. this.children = hasChild ? [] : null;
  195. }
  196. public get getIcon(): string | undefined {
  197. return this.icon;
  198. }
  199. public set setIcon(v: string) {
  200. this.icon = v;
  201. }
  202. public get getPath(): string | undefined {
  203. return this.path;
  204. }
  205. public set setPath(v: string) {
  206. this.path = v;
  207. }
  208. }
  209. const { userInfo } = useUserStore();
  210. const recursiveFunTree = (children: Menu.MenuOptions[]): TreeNode[] => {
  211. let treeArray: TreeNode[] = [];
  212. for (const menu of children) {
  213. if (menu.meta?.isHide) continue;
  214. let treeNode = new TreeNode(menu.name, menu.meta?.title, isNullOrUnDef(menu.children));
  215. if (isNullOrUnDef(menu.component)) treeNode.path = menu.path;
  216. if (isNullOrUnDef(menu.meta?.icon)) treeNode.icon = menu.meta?.icon;
  217. if (!isNullOrUnDef(menu.meta?.funid))
  218. treeNode.disabled = userInfo?.rsltFunids && !userInfo?.rsltFunids.includes(menu.meta?.funid);
  219. if (isNullOrUnDef(menu.children)) continue;
  220. treeArray.push(treeNode);
  221. // 递归子集
  222. treeNode.children = recursiveFunTree(menu.children);
  223. }
  224. return treeArray;
  225. };
  226. console.log("getQuickEnterFuncTree menuList :>> ", menuList);
  227. let functionTree = recursiveFunTree(menuList);
  228. return functionTree;
  229. }
  230. /**
  231. * @description 使用递归过滤出需要渲染在首页常用功能树路由 (需剔除 isHide == true 的菜单)
  232. * @param {Array} menuList 菜单列表
  233. * @returns {Array}
  234. * */
  235. export function getQuickEnterFuncTreeRouter(menuList: Menu.MenuOptions[]): Map<string, { [key: string]: any }> {
  236. console.log("getQuickEnterFuncTreeRouter menuList :>> ", menuList);
  237. class TreeNode {
  238. key: string;
  239. name: string;
  240. icon?: string;
  241. path: string;
  242. funid: number | string;
  243. constructor(key: string, label: string, path: string, icon?: string, funid?: number) {
  244. this.key = key;
  245. this.name = label;
  246. this.path = path;
  247. this.icon = icon || "";
  248. this.funid = funid ?? 0;
  249. }
  250. }
  251. const recursiveFunTree = (children: Menu.MenuOptions[]): Map<string, TreeNode> => {
  252. let routerMap: Map<string, TreeNode> = new Map();
  253. for (const menu of children) {
  254. // 跳过已隐藏的菜单 | 跳过不存在component的
  255. if (!(menu.meta?.isHide || isNullOrUnDef(menu.component))) {
  256. //
  257. let treeNode = new TreeNode(menu.name, menu.meta?.title, menu.path, menu.meta?.icon, menu.meta?.funid);
  258. routerMap.set(menu.name, treeNode);
  259. }
  260. if (isNullOrUnDef(menu.children)) continue;
  261. // 递归子集
  262. routerMap = new Map([...routerMap, ...recursiveFunTree(menu.children)]);
  263. }
  264. return routerMap;
  265. };
  266. let routerMap = recursiveFunTree(menuList);
  267. return routerMap;
  268. }
  269. /**
  270. * @description 使用递归处理路由菜单 path,生成一维数组 (第一版本地路由鉴权会用到,该函数暂未使用)
  271. * @param {Array} menuList 所有菜单列表
  272. * @param {Array} menuPathArr 菜单地址的一维数组 ['**','**']
  273. * @returns {Array}
  274. */
  275. export function getMenuListPath(menuList: Menu.MenuOptions[], menuPathArr: string[] = []): string[] {
  276. for (const item of menuList) {
  277. if (typeof item === "object" && item.path) menuPathArr.push(item.path);
  278. if (item.children?.length) getMenuListPath(item.children, menuPathArr);
  279. }
  280. return menuPathArr;
  281. }
  282. /**
  283. * @description 递归查询当前 path 所对应的菜单对象 (该函数暂未使用)
  284. * @param {Array} menuList 菜单列表
  285. * @param {String} path 当前访问地址
  286. * @returns {Object | null}
  287. */
  288. export function findMenuByPath(menuList: Menu.MenuOptions[], path: string): Menu.MenuOptions | null {
  289. for (const item of menuList) {
  290. if (item.path === path) return item;
  291. if (item.children) {
  292. const res = findMenuByPath(item.children, path);
  293. if (res) return res;
  294. }
  295. }
  296. return null;
  297. }
  298. /**
  299. * @description 使用递归过滤需要缓存的菜单 name (该函数暂未使用)
  300. * @param {Array} menuList 所有菜单列表
  301. * @param {Array} keepAliveNameArr 缓存的菜单 name ['**','**']
  302. * @returns {Array}
  303. * */
  304. export function getKeepAliveRouterName(menuList: Menu.MenuOptions[], keepAliveNameArr: string[] = []) {
  305. menuList.forEach(item => {
  306. item.meta.isKeepAlive && item.name && keepAliveNameArr.push(item.name);
  307. item.children?.length && getKeepAliveRouterName(item.children, keepAliveNameArr);
  308. });
  309. return keepAliveNameArr;
  310. }
  311. /**
  312. * @description 格式化表格单元格默认值 (el-table-column)
  313. * @param {Number} row 行
  314. * @param {Number} col 列
  315. * @param {*} callValue 当前单元格值
  316. * @returns {String}
  317. * */
  318. export function formatTableColumn(row: number, col: number, callValue: any) {
  319. // 如果当前值为数组,使用 / 拼接(根据需求自定义)
  320. if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "";
  321. return callValue ?? "";
  322. }
  323. /**
  324. * @description 处理值无数据情况
  325. * @param {*} callValue 需要处理的值
  326. * @returns {String}
  327. * */
  328. export function formatValue(callValue: any) {
  329. // 如果当前值为数组,使用 / 拼接(根据需求自定义)
  330. if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "";
  331. return callValue ?? "";
  332. }
  333. /**
  334. * @description 处理 prop 为多级嵌套的情况,返回的数据 (列如: prop: user.name)
  335. * @param {Object} row 当前行数据
  336. * @param {String} prop 当前 prop
  337. * @returns {*}
  338. * */
  339. export function handleRowAccordingToProp(row: { [key: string]: any }, prop: string, ...args: any) {
  340. if (args[0]) {
  341. return row[prop] && formatFn[args[0]] ? formatFn[args[0]]({ val: row[prop], format: args[0] }) : "";
  342. }
  343. if (!prop.includes(".")) return row[prop] ?? "";
  344. prop.split(".").forEach(item => (row = row[item] ?? ""));
  345. return row;
  346. }
  347. // 工具函数:解析 formatType
  348. export function parseFormatType(formatType: string) {
  349. const match = formatType.match(/^([a-zA-Z]+)(\d*)$/);
  350. return {
  351. fnName: match?.[1] || formatType,
  352. digits: match?.[2] ? parseInt(match[2], 10) : 2
  353. };
  354. }
  355. /**
  356. * @description 处理 prop,当 prop 为多级嵌套时 ==> 返回最后一级 prop
  357. * @param {String} prop 当前 prop
  358. * @returns {String}
  359. * */
  360. export function handleProp(prop: string) {
  361. const propArr = prop.split(".");
  362. if (propArr.length == 1) return prop;
  363. return propArr[propArr.length - 1];
  364. }
  365. /**
  366. * @description 根据枚举列表查询当需要的数据(如果指定了 label 和 value 的 key值,会自动识别格式化)
  367. * @param {String} callValue 当前单元格值
  368. * @param {Array} enumData 字典列表
  369. * @param {Array} fieldNames label && value && children 的 key 值
  370. * @param {String} type 过滤类型(目前只有 tag)
  371. * @returns {String}
  372. * */
  373. export function filterEnum(callValue: any, enumData?: any, fieldNames?: FieldNamesProps, type?: "tag") {
  374. const value = fieldNames?.value ?? "value";
  375. const label = fieldNames?.label ?? "label";
  376. const children = fieldNames?.children ?? "children";
  377. let filterData: { [key: string]: any } = {};
  378. // 判断 enumData 是否为数组
  379. if (Array.isArray(enumData)) filterData = findItemNested(enumData, callValue, value, children);
  380. // 判断是否输出的结果为 tag 类型
  381. if (type == "tag") {
  382. return filterData?.tagType ? filterData.tagType : "";
  383. } else {
  384. return filterData ? filterData[label] : "";
  385. }
  386. }
  387. /**
  388. * @description 递归查找 callValue 对应的 enum 值
  389. * */
  390. export function findItemNested(enumData: any, callValue: any, value: string, children: string) {
  391. return enumData.reduce((accumulator: any, current: any) => {
  392. if (accumulator) return accumulator;
  393. if (current[value] === callValue) return current;
  394. if (current[children]) return findItemNested(current[children], callValue, value, children);
  395. }, null);
  396. }
  397. // 日期格式化
  398. function formatDate({ val, format }: any) {
  399. return XEUtils.toDateString(val, format || "yyyy-MM-dd HH:mm:ss");
  400. }
  401. // 四舍五入金额,每隔3位逗号分隔,默认0位小数
  402. export function formatIntNumber({ val }: any, digits = 0) {
  403. if (isNaN(Number(val))) return val;
  404. return XEUtils.commafy(XEUtils.toNumber(val), { digits });
  405. }
  406. // 四舍五入金额,每隔3位逗号分隔,默认2位数
  407. export function formatAmount3({ val }: any, digits = 2) {
  408. if (isNaN(Number(val))) return val;
  409. return XEUtils.commafy(XEUtils.toNumber(val), { digits });
  410. }
  411. // 向下舍入,默认两位数
  412. export function formatCutNumber({ val }: any, digits = 2) {
  413. if (isNaN(Number(val))) return val;
  414. if (Number(val) == 0) return 0;
  415. return XEUtils.toFixed(XEUtils.floor(val, digits), digits);
  416. }
  417. // 向下舍入,默认两位数
  418. function formatCutNumber3({ val }: any, digits = 3) {
  419. if (isNaN(Number(val))) return val;
  420. return XEUtils.toFixed(XEUtils.floor(val, digits), digits);
  421. }
  422. // 四舍五入,默认两位数
  423. export function formatFixedNumber({ val }: any, digits = 2) {
  424. if (isNaN(Number(val))) return val;
  425. return XEUtils.toFixed(XEUtils.round(val, digits), digits);
  426. }
  427. // 百分比:四舍五入金额,每隔3位逗号分隔,默认2位
  428. function formatPercent({ val }: any, digits = 2) {
  429. if (isNaN(Number(val))) return val;
  430. return XEUtils.commafy(XEUtils.toNumber(val) * 100, { digits }) + "%";
  431. }
  432. /**
  433. * @description 格式化调用函数对照
  434. */
  435. const formatFn: any = {
  436. "yyyy-MM-dd": formatDate,
  437. "yyyy-MM-dd HH:mm:ss": formatDate,
  438. "yyyy-MM-dd HH:mm": formatDate,
  439. "MM/dd/yyyy": formatDate,
  440. "MM/dd": formatDate,
  441. "dd/MM/yyyy": formatDate,
  442. "yyyy/MM/dd": formatDate,
  443. intNumber: formatIntNumber,
  444. cutNumber: formatCutNumber,
  445. amountNumber: formatAmount3,
  446. fixedNumber: formatFixedNumber,
  447. cutNumber3: formatCutNumber3,
  448. percent: formatPercent
  449. };
  450. /**
  451. * 一维,精简对象储存属性;支持数组型默认数据(关键字:field)
  452. * 逻辑:精简出与默认数据不同的属性,保留layoutAttr包含的字段
  453. * @param {Object} column 列数据
  454. * @param {String[]} layoutAttr 需要保留的数据名称
  455. * @param {Object | Array} layoutDefine 数据默认值 {...} Or [{field: xxx, ...}, ...]
  456. * @returns {Array} 返回精简后的对象
  457. */
  458. export function streamlineFunc(column: any, layoutAttr: string[], layoutDefine: any) {
  459. let streamlinedColumns = [];
  460. if (Array.isArray(layoutDefine)) {
  461. for (let item of column) {
  462. const matchingDefinition = layoutDefine.find(itm => itm.field === item.field);
  463. const relevantAttrs = layoutAttr.filter(attr => {
  464. const itemValue = get(item, attr);
  465. const defineValue = get(matchingDefinition, attr);
  466. // 排除某些属性,并检查值是否不同
  467. return !["field", "table", "datatype"].includes(attr) && defineValue !== itemValue;
  468. });
  469. // 将 "field" 包含在过滤后的属性中
  470. const attrsToPick = [...relevantAttrs, "field"];
  471. streamlinedColumns.push(pick(item, attrsToPick));
  472. }
  473. } else {
  474. for (let item of column) {
  475. const relevantAttrs = layoutAttr.filter(attr => {
  476. // 检查值是否与布局定义不同
  477. return item[attr] !== layoutDefine[attr];
  478. });
  479. streamlinedColumns.push(pick(item, relevantAttrs));
  480. }
  481. }
  482. return streamlinedColumns;
  483. }
  484. /**
  485. * 一维,精简对象储存属性
  486. * @param {Object} column 列数据
  487. * @param {String[]} layoutAttr 需要保留的数据名称
  488. * @param {Object} layoutDefine 数据默认值
  489. * @param {String} attr 列数据的二级属性
  490. * @returns {Object} 返回精简后的对象
  491. */
  492. export function streamlineAttrFunc(column: any, layoutAttr: string[], layoutDefine: any, attr: string) {
  493. let _map = new Map();
  494. for (let item of column) {
  495. let saveProp = cloneDeep(layoutAttr);
  496. if (item.basicinfo) {
  497. for (let key in layoutDefine) {
  498. if (item[attr].hasOwnProperty(key) && layoutDefine[key] === item[attr][key]) {
  499. saveProp.splice(saveProp.indexOf(key), 1);
  500. }
  501. }
  502. _map.set(item.field, pick(item[attr], saveProp));
  503. }
  504. }
  505. return _map;
  506. }
  507. /**
  508. * 提取目标数据差异属性
  509. * @param {Object} oriLayout 原始对象,带默认值
  510. * @param {Object} targetLayout 目标对象,需要提取差异的数据
  511. * @param {String[]} attrs 需要保留字符串名称,规则:仅需提供最底层属性名称
  512. * @param {String} arrayAttr 数组数据,关键字段,用于排序
  513. * @returns {Object}
  514. */
  515. export function getDifference(oriLayout: any, targetLayout: any, attrs: string[] = [], arrayAttr = "id") {
  516. const diff: any = {};
  517. // 检查oriLayout中的属性
  518. for (const key in oriLayout) {
  519. if (oriLayout.hasOwnProperty(key)) {
  520. // 如果targetLayout中没有这个属性,则将它添加到差异中
  521. if (!targetLayout.hasOwnProperty(key)) {
  522. diff[key] = oriLayout[key];
  523. } else {
  524. // 如果targetLayout中也有这个属性,则比较它们的值
  525. if (Array.isArray(oriLayout[key])) {
  526. // 比较数组
  527. // 调整顺序,根据目标对象,调整原始对象数组顺序
  528. let _oriLayout = oriLayout[key];
  529. if (arrayAttr) {
  530. _oriLayout = oriLayout[key].slice().sort((a: any, b: any) => {
  531. let indexA = targetLayout[key].findIndex((item: any) => item[arrayAttr] === a[arrayAttr]);
  532. let indexB = targetLayout[key].findIndex((item: any) => item[arrayAttr] === b[arrayAttr]);
  533. return indexA - indexB;
  534. });
  535. }
  536. let arrAttr = [];
  537. for (const idx in _oriLayout) {
  538. console.log("oriLayout[key][idx] :>> ", _oriLayout[idx]);
  539. if (targetLayout[key][idx]) {
  540. const nestedDiff = getDifference(_oriLayout[idx], targetLayout[key][idx], attrs, arrayAttr);
  541. console.log(nestedDiff);
  542. if (Object.keys(nestedDiff).length > 0) {
  543. arrAttr.push(nestedDiff);
  544. }
  545. }
  546. }
  547. arrAttr.length && (diff[key] = arrAttr);
  548. } else if (typeof oriLayout[key] === "object" && typeof targetLayout[key] === "object") {
  549. // 递归比较对象
  550. console.log("key :>> ", key, oriLayout[key], targetLayout[key]);
  551. const nestedDiff = getDifference(oriLayout[key], targetLayout[key], attrs, arrayAttr);
  552. console.log("nestedDiff :>> ", key, nestedDiff);
  553. if (Object.keys(nestedDiff).length > 0) {
  554. console.log("nestedDiff :>> ", nestedDiff);
  555. diff[key] = nestedDiff;
  556. }
  557. } else if ((oriLayout[key] !== targetLayout[key] || key == arrayAttr) && attrs.includes(key)) {
  558. console.log("key :>> ", key);
  559. diff[key] = targetLayout[key];
  560. }
  561. }
  562. }
  563. }
  564. // 补充:检查targetLayout中的属性
  565. for (const key in targetLayout) {
  566. if (targetLayout.hasOwnProperty(key)) {
  567. // 如果oriLayout中没有这个属性,则将它添加到差异中
  568. if (
  569. !oriLayout.hasOwnProperty(key) ||
  570. (oriLayout.hasOwnProperty(key) && isObject(oriLayout[key]) && Object.keys(oriLayout[key]).length == 0)
  571. ) {
  572. diff[key] = targetLayout[key];
  573. }
  574. }
  575. }
  576. return diff;
  577. }
  578. /**
  579. * 读取数据到原始对象
  580. * @param {Object} oriLayout 原始对象,带默认值
  581. * @param {Object} targetLayout 差异的数据
  582. * @param {String} arrayAttr 数组数据,关键字段,用于排序
  583. * @returns {Object}
  584. */
  585. export function setDifference(oriLayout: any, loadLayout: any, arrayAttr: string = "id") {
  586. let _oriLayout = cloneDeep(oriLayout);
  587. for (const key in loadLayout) {
  588. if (loadLayout.hasOwnProperty(key)) {
  589. if (Array.isArray(loadLayout[key])) {
  590. // console.log("setDifference _oriLayout :>> ", _oriLayout);
  591. let _sortedObj1 = _oriLayout[key].slice().sort((a: any, b: any) => {
  592. let indexA = loadLayout[key].findIndex((item: any) => item[arrayAttr] === a[arrayAttr]);
  593. let indexB = loadLayout[key].findIndex((item: any) => item[arrayAttr] === b[arrayAttr]);
  594. if (indexA == -1 || indexB == -1) {
  595. return 0;
  596. } else {
  597. return indexA - indexB;
  598. }
  599. });
  600. // console.log("setDifference _sortedObj1 :>> ", _sortedObj1);
  601. // console.log("setDifference loadLayout[key] :>> ", loadLayout[key]);
  602. let arr = [];
  603. for (const idx in _sortedObj1) {
  604. // console.log("idx :>> ", idx);
  605. // console.log("loadLayout[key][idx] :>> ", loadLayout[key][idx]);
  606. // console.log("_sortedObj1[idx] :>> ", _sortedObj1[idx]);
  607. // if (loadLayout[key][idx] && _sortedObj1[idx] && loadLayout[key][idx][arrayAttr] == _sortedObj1[idx][arrayAttr]) {
  608. // arr.push(setDifference(_sortedObj1[idx], loadLayout[key][idx], arrayAttr));
  609. // }
  610. let itemIdx = loadLayout[key].findIndex((item: any) => item[arrayAttr] === _sortedObj1[idx][arrayAttr]);
  611. // console.log(itemIdx);
  612. let _loadLayout = itemIdx > -1 ? loadLayout[key][itemIdx] : {};
  613. arr.push(setDifference(_sortedObj1[idx], _loadLayout, arrayAttr));
  614. }
  615. // console.log("arr :>> ", arr);
  616. arr.length && (_oriLayout[key] = arr);
  617. } else if (typeof loadLayout[key] === "object") {
  618. if (!_oriLayout.hasOwnProperty(key)) {
  619. _oriLayout[key] = {};
  620. }
  621. _oriLayout[key] = setDifference(_oriLayout[key], loadLayout[key], arrayAttr);
  622. } else {
  623. _oriLayout[key] = loadLayout[key];
  624. }
  625. }
  626. }
  627. return _oriLayout;
  628. }
  629. /**
  630. * 金额缩略显示
  631. * @param s 数值
  632. * @param n 小数保留位数
  633. * @returns {string}
  634. */
  635. export function numberFormat(s: number, n: number) {
  636. n = n !== undefined && n >= 0 && n <= 20 ? n : 2;
  637. let sum = Math.abs(Number(s));
  638. let sumStr = parseFloat((sum + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
  639. let r = n ? "." + sumStr.split(".")[1] : "";
  640. let l = sumStr.split(".")[0].split("").reverse();
  641. let t = "";
  642. for (let i = 0; i < l.length; i++) {
  643. t += l[i] + ((i + 1) % 3 == 0 && i + 1 != l.length ? "," : "");
  644. }
  645. if (s < 0) return "-" + t.split("").reverse().join("") + r;
  646. return t.split("").reverse().join("") + r;
  647. }
  648. export const noop = () => {};
  649. /**
  650. * 获取目标属性,转换字符串对象
  651. * @param obj 目标值
  652. * @param str 目标字符串属性
  653. */
  654. export function convertStrToObj(obj: any, str?: string): any {
  655. console.log("convertStrToObj obj :>> ", obj);
  656. console.log("convertStrToObj str :>> ", str);
  657. if (!obj || JSON.stringify(obj) == "{}" || !str) return undefined;
  658. const keys = str.split(".");
  659. for (let i = 0; i < keys.length; i++) {
  660. const key = keys[i];
  661. obj[key] = obj[key] || undefined;
  662. obj = obj[key];
  663. }
  664. return obj;
  665. }
  666. /**
  667. * 时间格式转义, detaTime to Diff
  668. * @param data {value:Object}
  669. * @returns {Object}
  670. */
  671. export const getDiffToDate = (data: any) => {
  672. const today = dayjs(dayjs().format("YYYY-MM-DD"));
  673. for (const key in data) {
  674. if (key.indexOf("date") > -1 && key.indexOf("datetype") == -1) {
  675. if (isNumber(data[key])) {
  676. // number => string
  677. data[key] = today.add(data[key], "day").format("YYYY-MM-DD HH:mm:ss");
  678. } else if (isArray(data[key])) {
  679. // [number, number] => [string, string]
  680. data[key] = data[key].map((item: any) => {
  681. if (isNumber(item)) {
  682. return today.add(item, "day").format("YYYY-MM-DD HH:mm:ss");
  683. } else {
  684. return item;
  685. }
  686. });
  687. }
  688. }
  689. }
  690. console.log("getDiffToDate data :>> ", data);
  691. return data;
  692. };
  693. /**
  694. * 时间格式转义, Diff to DetaTime
  695. * @param data {value:Object}
  696. * @returns
  697. */
  698. export const setDateToDiff = (data: any) => {
  699. const today = dayjs(dayjs().format("YYYY-MM-DD"));
  700. for (const key in data) {
  701. if (key.indexOf("date") > -1 && key.indexOf("datetype") == -1) {
  702. if (isString(data[key]) || isDate(data[key])) {
  703. // string => number
  704. let newDate = dayjs(dayjs(data[key]).format("YYYY-MM-DD"));
  705. data[key] = newDate.diff(today, "day");
  706. } else if (isArray(data[key])) {
  707. // [string, string] => [number, number]
  708. data[key] = data[key].map((item: any) => {
  709. let newDate = dayjs(dayjs(item).format("YYYY-MM-DD"));
  710. return newDate.diff(today, "day");
  711. });
  712. }
  713. }
  714. }
  715. console.log("_habit setDateToDiff data :>> ", data);
  716. return data;
  717. };
  718. /**
  719. * 捕捉报错提示
  720. * @param err 报错含有,关键字:会话、已与服务器失联、找不到网络路径
  721. * @returns {Boolean}
  722. */
  723. export const ifErrorToLogin = (err: any) => {
  724. // 移除"已与服务器失联"关键字,只有L1失联会出现,会导致核价需要重新登陆
  725. // return err.indexOf("会话") >= 0 || err.indexOf("已与服务器失联") >= 0 || err.indexOf("找不到网络路径") >= 0;
  726. return err.indexOf("会话") >= 0 || err.indexOf("找不到网络路径") >= 0;
  727. };
  728. /**
  729. * 遍历树形结构的数据
  730. * @param tree 遍历的树形结构数据
  731. * @param callback 回调函数,用于处理遍历到的每个节点
  732. */
  733. export const traverseNode = (root: any, action: (node: any) => void) => {
  734. const queue = [root];
  735. while (queue.length > 0) {
  736. const node = queue.shift();
  737. action(node);
  738. if (node.children && node.children.length > 0) {
  739. for (const child of node.children) {
  740. queue.push(child);
  741. }
  742. }
  743. }
  744. };
  745. /*用正则表达式实现html转码*/
  746. export const htmlEncodeByRegExp = (str: string) => {
  747. let s = "";
  748. if (str.length == 0) return "";
  749. s = str.replace(/&/g, "&amp;");
  750. s = s.replace(/</g, "&lt;");
  751. s = s.replace(/>/g, "&gt;");
  752. s = s.replace(/ /g, "&nbsp;");
  753. s = s.replace(/\'/g, "&#39;");
  754. s = s.replace(/\"/g, "&quot;");
  755. return s;
  756. };
  757. /*用正则表达式实现html解码*/
  758. export const htmlDecodeByRegExp = (str: string) => {
  759. let s = "";
  760. if (str.length == 0) return "";
  761. s = str.replace(/&amp;/g, "&");
  762. s = s.replace(/&lt;/g, "<");
  763. s = s.replace(/&gt;/g, ">");
  764. s = s.replace(/&nbsp;/g, " ");
  765. s = s.replace(/&#39;/g, "'");
  766. s = s.replace(/&quot;/g, '"');
  767. return s;
  768. };
  769. /**
  770. * @description 加法
  771. * @param {Number} arg1 数1
  772. * @param {Number} arg2 数2
  773. * @returns
  774. */
  775. export const floatAdd = (arg1: any, arg2: any) => {
  776. let r1, r2, m;
  777. try {
  778. r1 = arg1.toString().split(".")[1].length;
  779. } catch (e) {
  780. r1 = 0;
  781. }
  782. try {
  783. r2 = arg2.toString().split(".")[1].length;
  784. } catch (e) {
  785. r2 = 0;
  786. }
  787. m = Math.pow(10, Math.max(r1, r2));
  788. return (Math.round(arg1 * m) + Math.round(arg2 * m)) / m;
  789. };
  790. export const floatAddMore = (arr: any) => {
  791. let r1,
  792. r2,
  793. m,
  794. sum = 0;
  795. for (const item of arr) {
  796. if (item == undefined || item == null) continue;
  797. r1 = sum.toString().split(".")[1]?.length ?? 0;
  798. r2 = item.toString().split(".")[1]?.length ?? 0;
  799. m = Math.pow(10, Math.max(r1, r2));
  800. sum = (Math.round(sum * m) + Math.round(item * m)) / m;
  801. }
  802. return sum;
  803. };
  804. /**
  805. * @description 减法
  806. * @param arg1
  807. * @param arg2
  808. * @returns
  809. */
  810. export const floatSub = (arg1: any, arg2: any) => {
  811. let r1, r2, m, n;
  812. try {
  813. r1 = arg1.toString().split(".")[1].length;
  814. } catch (e) {
  815. r1 = 0;
  816. }
  817. try {
  818. r2 = arg2.toString().split(".")[1].length;
  819. } catch (e) {
  820. r2 = 0;
  821. }
  822. m = Math.pow(10, Math.max(r1, r2));
  823. //动态控制精度长度
  824. n = r1 >= r2 ? r1 : r2;
  825. return ((Math.round(arg1 * m) - Math.round(arg2 * m)) / m).toFixed(n);
  826. };
  827. /**
  828. * @description 乘法
  829. * @param arg1
  830. * @param arg2
  831. * @returns
  832. */
  833. export const floatMul = (arg1: any, arg2: any) => {
  834. let m = 0,
  835. s1 = (arg1 ?? 0).toString(),
  836. s2 = (arg2 ?? 0).toString();
  837. try {
  838. m += s1.split(".")[1].length;
  839. } catch (e) {}
  840. try {
  841. m += s2.split(".")[1].length;
  842. } catch (e) {}
  843. return (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) / Math.pow(10, m);
  844. };
  845. export const floatMulMore = (arr: any) => {
  846. let m = 0,
  847. sum = 1;
  848. for (const item of arr) {
  849. if (item) {
  850. let s1 = item.toString();
  851. try {
  852. m += s1.split(".")[1].length;
  853. } catch (e) {}
  854. sum = sum * Number(s1.replace(".", ""));
  855. }
  856. }
  857. return Number(sum) / Math.pow(10, m);
  858. };
  859. /**
  860. * @description 除法
  861. * @param arg1
  862. * @param arg2
  863. * @returns
  864. */
  865. export const floatDiv = (arg1: any, arg2: any) => {
  866. let t1 = 0,
  867. t2 = 0,
  868. r1,
  869. r2;
  870. try {
  871. t1 = arg1.toString().split(".")[1].length;
  872. } catch (e) {}
  873. try {
  874. t2 = arg2.toString().split(".")[1].length;
  875. } catch (e) {}
  876. r1 = Number(arg1.toString().replace(".", ""));
  877. r2 = Number(arg2.toString().replace(".", ""));
  878. return (r1 / r2) * Math.pow(10, t2 - t1);
  879. };
  880. /**
  881. * @description 文件类型转化base64类型
  882. * @param type 文件类型
  883. * @returns base64类型
  884. */
  885. export const getBase64Type = (type: string) => {
  886. switch (type.toLowerCase()) {
  887. case "txt":
  888. return "data:text/plain;base64,";
  889. case "doc":
  890. return "data:application/msword;base64,";
  891. case "docx":
  892. return "data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,";
  893. case "xls":
  894. return "data:application/vnd.ms-excel;base64,";
  895. case "xlsx":
  896. return "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,";
  897. case "pdf":
  898. return "data:application/pdf;base64,";
  899. case "pptx":
  900. return "data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,";
  901. case "ppt":
  902. return "data:application/vnd.ms-powerpoint;base64,";
  903. case "png":
  904. return "data:image/png;base64,";
  905. case "jpg":
  906. return "data:image/jpeg;base64,";
  907. case "gif":
  908. return "data:image/gif;base64,";
  909. case "svg":
  910. return "data:image/svg+xml;base64,";
  911. case "ico":
  912. return "data:image/x-icon;base64,";
  913. case "bmp":
  914. return "data:image/bmp;base64,";
  915. default:
  916. return "";
  917. }
  918. };
  919. /**
  920. * @description 自定义函数检查className中是否包含需要的类名
  921. */
  922. export const classNameIncludes = (element: any, target: string[]) => {
  923. if (!element) return false;
  924. let _className: string[] = [];
  925. let parent = element.parentElement;
  926. // 获取当前元素的类名
  927. if (typeof element.className === "string") {
  928. _className = element.className.split(/\s+/g);
  929. }
  930. // 如果父元素存在,则获取父元素的类名
  931. while (parent) {
  932. if (parent.classList) {
  933. // 获取当前父元素的所有类名并添加到数组中
  934. Array.from(parent.classList).forEach((className: any) => {
  935. if (!_className.includes(className)) {
  936. _className.push(className);
  937. }
  938. });
  939. }
  940. // 移动到下一个父元素
  941. parent = parent.parentElement;
  942. }
  943. return target.some(item => _className.indexOf(item) > -1);
  944. };
  945. /**
  946. * @description 倒计时
  947. * @param {number} value 剩余秒数
  948. * @returns {string} 倒计时 00:00:30,
  949. */
  950. export const countDownTime = (value: number) => {
  951. // 处理数字类型的秒数
  952. if (value <= 0) {
  953. return "";
  954. }
  955. const days = Math.floor(value / 86400);
  956. const hours = Math.floor((value % 86400) / 3600)
  957. .toString()
  958. .padStart(2, "0");
  959. const minutes = Math.floor((value % 3600) / 60)
  960. .toString()
  961. .padStart(2, "0");
  962. const seconds = (value % 60).toString().padStart(2, "0");
  963. let text = "";
  964. if (days > 365) {
  965. text = `${Math.floor(days / 365)}年`;
  966. } else if (days > 31) {
  967. text = `${Math.floor(days / 31)}月`;
  968. } else if (days > 0) {
  969. text = `${days}天`;
  970. } else {
  971. text = `${hours}:${minutes}:${seconds}`;
  972. }
  973. return text;
  974. };
  975. const parseTime = (time: any, cFormat?: string) => {
  976. let format = cFormat ?? `{y}-{m}-{d} {h}:{i}:{s}`;
  977. let date;
  978. if (typeof time === "object") {
  979. date = time;
  980. } else {
  981. if (typeof time === "string") {
  982. if (/^[0-9]+$/.test(time)) {
  983. time = parseInt(time);
  984. } else {
  985. time = time.replace(new RegExp(/-/gm), "/");
  986. }
  987. }
  988. if (typeof time === "number" && time.toString().length === 10) {
  989. time = time * 1000;
  990. }
  991. date = new Date(time);
  992. }
  993. const formatObj: any = {
  994. y: date.getFullYear(),
  995. m: date.getMonth() + 1,
  996. d: date.getDate(),
  997. h: date.getHours(),
  998. i: date.getMinutes(),
  999. s: date.getSeconds(),
  1000. a: date.getDay()
  1001. };
  1002. const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
  1003. const value = formatObj[key];
  1004. // Note: getDay() returns 0 on Sunday
  1005. if (key === "a") {
  1006. return ["日", "一", "二", "三", "四", "五", "六"][value];
  1007. }
  1008. return value.toString().padStart(2, "0");
  1009. });
  1010. return time_str;
  1011. };
  1012. /**
  1013. * 最近时间-时差
  1014. * @param {number} time
  1015. * @param {string} option
  1016. * @returns {Number} (秒)
  1017. */
  1018. export const formatTime = function (time: string | number, option: string, ifshort: boolean, nowtime?: string) {
  1019. const _time = new Date(time);
  1020. const d = new Date(time);
  1021. const now = nowtime ? new Date(nowtime) : new Date();
  1022. const diff = (now.getTime() - d.getTime()) / 1000;
  1023. if (ifshort) {
  1024. if (diff < 0) {
  1025. // flutter
  1026. } else if (diff < 30) {
  1027. return "刚刚";
  1028. } else if (diff < 3600) {
  1029. // less 1 hour
  1030. return Math.ceil(diff / 60) + "分钟前";
  1031. } else if (diff < 3600 * 6) {
  1032. // 1~6小时
  1033. return Math.ceil(diff / 3600) + "小时前";
  1034. }
  1035. let hourMins = parseTime(_time, "{h}:{i}:{s}");
  1036. let isSameDay = now.setHours(0, 0, 0, 0) - d.setHours(0, 0, 0, 0);
  1037. switch (isSameDay) {
  1038. case 0:
  1039. return `今天 ${hourMins}`;
  1040. case 86400000:
  1041. return `昨天 ${hourMins}`;
  1042. // case 172800000:
  1043. // return `前天 ${hourMins}`
  1044. }
  1045. if (now.getFullYear() == d.getFullYear()) {
  1046. return parseTime(_time, "{m}-{d} {h}:{i}:{s}");
  1047. } else {
  1048. return parseTime(_time, "{y}-{m}-{d} {h}:{i}:{s}");
  1049. }
  1050. }
  1051. if (option) {
  1052. return parseTime(_time, option);
  1053. }
  1054. };
  1055. const funcGetFormulaValue = (item, tgList, fieldList, valueList) => {
  1056. let _paras = item?.formula
  1057. .replace(/【/g, "[")
  1058. .replace(/】/g, "]")
  1059. .match(/\[([^\]]*)\]/g);
  1060. let resultFormula = item?.formula.replace(/【/g, "[").replace(/】/g, "]");
  1061. _paras.forEach(part => {
  1062. // console.log(part);
  1063. let fieldName = part.slice(1, -1); // 去掉方括号
  1064. // console.log(fieldName);
  1065. let sumVal = tgList.find(f => {
  1066. if (f.field === fieldName) {
  1067. return f;
  1068. } else if (f.label === fieldName) {
  1069. fieldName = f.field;
  1070. return f;
  1071. }
  1072. });
  1073. !sumVal &&
  1074. (sumVal = fieldList.find(f => {
  1075. if (f.field === fieldName) {
  1076. return f;
  1077. } else if (f.label === fieldName) {
  1078. fieldName = f.field;
  1079. return f;
  1080. }
  1081. }));
  1082. if (sumVal) {
  1083. let res = valueList[fieldName];
  1084. let _formula;
  1085. if (sumVal?.formula) {
  1086. _formula = sumVal.formula.replace(part, `${valueList[fieldName]}`);
  1087. }
  1088. // console.log(fieldName, _formula, sumVal);
  1089. if (_formula && _formula.indexOf("[") > -1) {
  1090. let _res = formulaPartsFormula([{ formula: _formula }], fieldList, valueList, tgList);
  1091. res = `(${_res[0]})`;
  1092. }
  1093. if (typeof res === "undefined") {
  1094. ElMessage.warning("接口未提供数据,请检查:" + fieldName);
  1095. console.error("接口未提供数据,请检查:" + fieldName);
  1096. return;
  1097. }
  1098. resultFormula = resultFormula.replace(part, res);
  1099. } else {
  1100. valueList[fieldName] && (resultFormula = resultFormula.replace(part, `${valueList[fieldName]}`));
  1101. }
  1102. });
  1103. return resultFormula;
  1104. };
  1105. export const formulaPartsFormula = (formulaList, fieldList, valueList, tgList = formulaList) => {
  1106. let result = [];
  1107. formulaList.map((item, index) => {
  1108. if (item?.formula) {
  1109. let res = funcGetFormulaValue(item, tgList, fieldList, valueList);
  1110. result.push(res);
  1111. }
  1112. });
  1113. return result;
  1114. };
  1115. /**
  1116. * @description 公式计算
  1117. * @param formulaList 公式列表 [{field: 'a', formula: '[b] + [c]'}]
  1118. * @param fieldList 字段列表 [{field: 'a', label: '字段A'}, {field: 'a', formula: '[b] + [c]'}]
  1119. * @param valueList 值列表 {a: 1, b: 2, c: 3}
  1120. * @returns {number[]} 计算结果 [1]
  1121. */
  1122. export const calculateFormula = async (formulaList, fieldList, valueList) => {
  1123. let resultFormula: any[] = formulaPartsFormula(formulaList, fieldList, valueList);
  1124. console.log("resultFormula: ", resultFormula);
  1125. try {
  1126. let res = await GetFormulaCompute({
  1127. formulas: resultFormula
  1128. });
  1129. if (res.values.length == 0) return [];
  1130. let result = formulaList.map((item, index) => {
  1131. let _res = res.values[index] ?? 0;
  1132. return {
  1133. ...item,
  1134. value: _res
  1135. };
  1136. });
  1137. return result;
  1138. } catch (error) {
  1139. console.error("Formula evaluation error:", error);
  1140. return [];
  1141. }
  1142. };
  1143. /**
  1144. * @description LjVxeTable获取当前表格选中数据;默认第一条记录
  1145. * @param targetRef 表格ref
  1146. * @returns {$table, curRecords} 当前表格对象,当前选中数据
  1147. */
  1148. export const getCurrentRecords = (targetRef: any) => {
  1149. const $table = targetRef.element;
  1150. const _records = $table.getCheckboxRecords() ?? [];
  1151. const _cRecords = $table.getCurrentRecord() ?? null;
  1152. let curRecords = [];
  1153. if ($table) {
  1154. if (_records.length) {
  1155. // 获取勾选列表
  1156. curRecords = _records;
  1157. } else if (_cRecords) {
  1158. // 获取当前选中数据
  1159. curRecords = [_cRecords];
  1160. } else {
  1161. // 默认获取第一条记录
  1162. let fullData = $table.getTableData().fullData;
  1163. if (fullData.length) {
  1164. curRecords = [fullData[0]];
  1165. $table.setCurrentRow(fullData[0]);
  1166. }
  1167. }
  1168. }
  1169. return { $table, curRecords };
  1170. };
  1171. export const transformTreeData = (data: any) => {
  1172. return data?.map(node => ({
  1173. ...node.data,
  1174. children: node.children ? transformTreeData(node.children) : []
  1175. }));
  1176. };
  1177. /**
  1178. * @description 过滤掉末尾的0
  1179. * @param data
  1180. * @returns
  1181. */
  1182. export const isFilterPrice = (data, digits?: number) => {
  1183. if (data == null || data == undefined) {
  1184. return "0";
  1185. }
  1186. let _digits = digits ?? 2;
  1187. let val = formatAmount3({ val: data }, _digits);
  1188. // 过滤掉末尾的0
  1189. let arr = val.split("");
  1190. while (arr[arr.length - 1] === "0") {
  1191. arr.pop();
  1192. }
  1193. return arr.join("");
  1194. };
  1195. /**
  1196. * @description 计算vxetable的合并单元格
  1197. * @param $table 表对象
  1198. * @param fields 计算合并的字段明
  1199. * @returns 需要合并的单元格数组
  1200. */
  1201. export const autoMergeCells = ($table: any, fields: string[]) => {
  1202. let result: any = [];
  1203. let columns = $table.getColumns();
  1204. let { visibleData: data } = $table.getTableData();
  1205. // console.log("mergeCells data :>> ", columns, data);
  1206. data.map((item: any, rowIndex: number) => {
  1207. for (const key in item) {
  1208. if (fields.includes(key)) {
  1209. let rowspan = 1;
  1210. let currentVal = "";
  1211. let lastVal = "";
  1212. let colIndex = 0;
  1213. colIndex = columns.findIndex((item: any) => item.field == key);
  1214. currentVal = item[key];
  1215. if (rowIndex - 1 >= 0) {
  1216. lastVal = data[rowIndex - 1][key];
  1217. }
  1218. // console.log("lastVal currentVal:>> ", colIndex, key, lastVal, currentVal);
  1219. // if (rowIndex > 0) {
  1220. // if (!lastVal) {
  1221. if (lastVal != currentVal) {
  1222. // 计算合并行数
  1223. let _span = 0;
  1224. for (let i = rowIndex + 1; i < data.length; i++) {
  1225. if (data[i][key] && currentVal && data[i][key] == currentVal) {
  1226. _span++;
  1227. } else {
  1228. break;
  1229. }
  1230. }
  1231. rowspan += _span;
  1232. } else {
  1233. rowspan = 0;
  1234. }
  1235. // console.log("rowspan :>> ", key, lastVal, currentVal, rowspan);
  1236. // }
  1237. // }
  1238. if (rowspan > 0) {
  1239. result.push({
  1240. row: rowIndex,
  1241. col: colIndex,
  1242. rowspan: rowspan,
  1243. colspan: 1
  1244. });
  1245. }
  1246. }
  1247. }
  1248. });
  1249. return result;
  1250. };
  1251. export const text2Formula = (str: any) => {
  1252. let rt = [];
  1253. if (str.includes("以上") || str.includes("以下")) {
  1254. // 匹配 "上下拼接X及以上Y以下"
  1255. let rangeMatch = str.match(/(\d+)及以下/);
  1256. if (rangeMatch) {
  1257. const value = parseInt(rangeMatch[1], 10);
  1258. rt.push(`value <= ${value}`);
  1259. } else {
  1260. rangeMatch = str.match(/(\d+)以下/);
  1261. if (rangeMatch) {
  1262. const value = parseInt(rangeMatch[1], 10);
  1263. rt.push(`value < ${value}`);
  1264. }
  1265. }
  1266. rangeMatch = str.match(/(\d+)及以上/);
  1267. if (rangeMatch) {
  1268. const value = parseInt(rangeMatch[1], 10);
  1269. rt.push(`value >= ${value}`);
  1270. } else {
  1271. rangeMatch = str.match(/(\d+)以上/);
  1272. if (rangeMatch) {
  1273. const value = parseInt(rangeMatch[1], 10);
  1274. rt.push(`value > ${value}`);
  1275. }
  1276. }
  1277. } else {
  1278. // 匹配 "上下拼接X"
  1279. const exactMatch = str.match(/(\d+)/);
  1280. if (exactMatch) {
  1281. const value = parseInt(exactMatch[1], 10);
  1282. rt.push(`value == ${value}`);
  1283. }
  1284. }
  1285. if (rt.length > 0) {
  1286. return rt.join(" && ");
  1287. }
  1288. throw new Error(`无法解析规则: ${str}`);
  1289. };
  1290. /**
  1291. * @description 事件: 一键复制
  1292. */
  1293. export const copyFunc = (data: any) => {
  1294. const input = document.createElement("textarea");
  1295. input.value = data;
  1296. document.body.appendChild(input);
  1297. input.select();
  1298. document.execCommand("Copy");
  1299. document.body.removeChild(input);
  1300. ElMessage({
  1301. type: "success",
  1302. message: "复制成功"
  1303. });
  1304. };