zTool.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. import _ from 'lodash'
  2. import moment from 'moment'
  3. let zTool = {
  4. /**
  5. * @description 表单验证
  6. * @param formRef 组件
  7. * @param callBack 回调方法
  8. */
  9. validate(formRef, callBack) {
  10. formRef.validate(valid => {
  11. if (!!valid && callBack) {
  12. callBack()
  13. }
  14. });
  15. },
  16. /**
  17. * @description 表单验证
  18. * @param formRefs 组件
  19. * @param callBack 回调方法
  20. */
  21. validateForms(formRefs, callBack) {
  22. let promises = [];
  23. for (let i = 0; i < formRefs.length; i++) {
  24. promises.push(new Promise((resolve) => { formRefs[i].validate(valid => { if (valid) { resolve(); } }) }));
  25. }
  26. Promise.all(promises).then(() => { if (callBack) { callBack() } }).catch(()=>{})
  27. },
  28. /**
  29. * @description 表单字段验证
  30. * @param formRef 组件
  31. * @param callBack 回调方法
  32. * @param fields 字段
  33. */
  34. validateFields(formRef, callBack, ...fields) {
  35. for (let i = 0; i < fields.length; i++) {
  36. formRef.validateField(fields[i],valid => {
  37. if (!!valid && callBack && ( i +1 ) === fields.length) {
  38. callBack()
  39. }
  40. })
  41. }
  42. },
  43. /**
  44. * @description 启用禁用
  45. * @param ref 组件
  46. * @param apiObj API对象
  47. * @param row 行数据
  48. * @param index 行号
  49. * @param props 属性数组
  50. */
  51. switch(ref, apiObj, row, index, props = ['id']) {
  52. if (index === -1) {
  53. return;
  54. }
  55. let params = { isActived: row.isActived };
  56. for (let prop of props) {
  57. params[`${prop}`] = row[`${prop}`]
  58. }
  59. this.loading(ref);
  60. apiObj.put(params).then(() => {
  61. ref.$message.success(row.isActived === 0 ? '禁用成功' : '启用成功');
  62. ref.searchFun();
  63. }).catch(()=>{}).finally(() => {
  64. this.loadingHidden(ref);
  65. });
  66. },
  67. /**
  68. * @description 保存
  69. * @param ref 组件
  70. * @param apiObj API对象
  71. * @param params 保存参数
  72. * @param msg 提示信息
  73. * @param callBack 回调方法
  74. * @param isMessage 是否提示
  75. * @param isCloseDialog 是否关闭弹窗
  76. * @param isSearchFun 是否刷新页面
  77. */
  78. save(ref, apiObj, params, msg = '操作成功', callBack, isMessage = true, isCloseDialog = true, isSearchFun = true) {
  79. this.loading(ref);
  80. apiObj.post(params).then(() => {
  81. if (isMessage) {
  82. ref.$message.success(msg);
  83. }
  84. if (isCloseDialog) {
  85. ref.closeDialog();
  86. }
  87. if (isSearchFun) {
  88. ref.searchFun();
  89. }
  90. if (callBack) {
  91. callBack();
  92. }
  93. }).catch(()=>{}).finally(() => {
  94. this.loadingHidden(ref);
  95. })
  96. },
  97. /**
  98. * @description 响应赋值
  99. * @param ref 组件f
  100. * @param apiObj API对象
  101. * @param name 赋值的属性
  102. * @param params 参数
  103. * @param callBack 回调方法
  104. */
  105. resSetValue(ref, apiObj, name, params = {}, callBack, isLoad = false) {
  106. let obj = getObjByStrName(ref, name)
  107. let attr = getAttrByStrName(ref, name)
  108. if (obj[`${attr}`] === undefined) {
  109. return
  110. }
  111. if (isLoad) {
  112. this.loading(ref);
  113. }
  114. apiObj.get(params).then((res) => {
  115. obj[`${attr}`] = res;
  116. if (callBack) {
  117. callBack(res)
  118. }
  119. }).catch(()=>{}).finally(() => {
  120. if (isLoad) {
  121. this.loadingHidden(ref);
  122. }
  123. })
  124. },
  125. /**
  126. * @description Number格式化
  127. * @param number Number
  128. * @param afterPointDigit 小数点后位数
  129. * @param useThousandSign 是否使用千位符
  130. * @param noExistDisplay 数字不存在时的展示值
  131. * @param zeroDisplayNegative 数字0展示为负数
  132. * @return number 格式化后的时数字/字符串
  133. */
  134. numberFormat(number, afterPointDigit, useThousandSign = true, noExistDisplay, zeroDisplayNegative) {
  135. if (number !== 0 && !number) {
  136. if (noExistDisplay) {
  137. return noExistDisplay
  138. } else {
  139. return number
  140. }
  141. }
  142. number = Number(number)
  143. if (afterPointDigit === 0 || afterPointDigit) {
  144. number = number.toFixed(afterPointDigit)
  145. }
  146. if (useThousandSign) {
  147. number = number.toString()
  148. if (number.indexOf('.') >= 0) {
  149. number = number.replace(/(\d{1,3})(?=(\d{3})+(?:\.))/g, '$1,')
  150. } else {
  151. number = number.replace(/(\d{1,3})(?=(\d{3})+(?:$))/g, '$1,')
  152. }
  153. }
  154. if (zeroDisplayNegative && Number(number) == 0) {
  155. number = '-' + number
  156. }
  157. return number
  158. },
  159. /**
  160. * @description 改变输入值
  161. * @param ref 组件
  162. * @param name 属性名称
  163. * @param type change类型 具体类型如下
  164. * STR_1允许输入任意字符; STR_2允许输入数字字母下划线和汉字; STR_3允许输入数字字母下划线;
  165. * NUM_1允许输入数字; NUM_2允许输入数字(去除数字前方的0); NUM_3允许输入数字和小数点(去除数字前方多余的0); NUM_4允许输入数字(去除数字前方多余的0);NUM_5允许输入负数,数字和小数点(去除数字前方多余的0);
  166. * NULL_XX 不允许非法字符
  167. * @param max1 最大输入长度 NUM类型时为小数点前数字位数
  168. * @param max2 小数点后数字位数
  169. */
  170. changeInput(ref, name, type, max1, max2) {
  171. let obj = getObjByStrName(ref, name)
  172. let attr = getAttrByStrName(ref, name)
  173. if (obj[`${attr}`] === undefined) {
  174. return
  175. }
  176. if (obj[`${attr}`] === null) {
  177. return
  178. }
  179. obj[`${attr}`] = obj[`${attr}`].toString().trim();
  180. if (type === 'STR_1') {
  181. obj[`${attr}`] = obj[`${attr}`].substring(0, max1)
  182. } else if (type === 'STR_2') {
  183. obj[`${attr}`] = obj[`${attr}`].replace(/[^\w_/.*\u4e00-\u9fa5]/g, '').substring(0, max1)
  184. } else if (type === 'STR_3') {
  185. obj[`${attr}`] = obj[`${attr}`].replace(/[^\w_]/g, '').substring(0, max1)
  186. } else if (type === 'NUM_1') {
  187. obj[`${attr}`] = obj[`${attr}`].replace(/[^\d]/g, '').substring(0, max1)
  188. } else if (type === 'NUM_2') {
  189. obj[`${attr}`] = obj[`${attr}`]
  190. .replace(/[^\d]/g, '')
  191. .replace(/\b(0+)/g, '')
  192. .substring(0, max1)
  193. } else if (type === 'NUM_3') {
  194. let num = obj[`${attr}`]
  195. num = num
  196. .replace(/[^\d.]/g, '')
  197. .replace('.', '$#$')
  198. .replace(/\./g, '')
  199. .replace('$#$', '.')
  200. .replace(/^\./g, '')
  201. if (num.indexOf('.') === -1) {
  202. num = num == 0 ? num.replace(/\b(0+)/g, '0') : num.replace(/\b(0+)/g, '').substring(0, max1)
  203. } else {
  204. num = num.substring(0, num.indexOf('.')).substring(0, max1) + '.' + num.substring(num.indexOf('.') + 1).substring(0, max2)
  205. }
  206. obj[`${attr}`] = num
  207. } else if (type === 'NUM_4') {
  208. let num = obj[`${attr}`]
  209. obj[`${attr}`] = num == 0 ? num.replace(/[^\d]/g, '').replace(/\b(0+)/g, '0') : num.replace(/[^\d]/g, '').replace(/\b(0+)/g, '').substring(0, max1)
  210. } else if (type === 'NUM_5') {
  211. let num = obj[`${attr}`]
  212. num = num
  213. .replace(/[^\d.-]/g, '')
  214. .replace('.', '$#$')
  215. .replace(/\./g, '')
  216. .replace('$#$', '.')
  217. .replace(/^\./g, '')
  218. .replace(/-{2,}/g, '-')
  219. .replace(/(?<=-)(.*?)(-)/, '')
  220. if (num.indexOf('.') === -1) {
  221. num = num == 0 ? num.replace(/\b(0+)/g, '0') : num.replace(/\b(0+)/g, '').substring(0, max1)
  222. } else {
  223. num = num.substring(0, num.indexOf('.')).substring(0, max1) + '.' + num.substring(num.indexOf('.') + 1).substring(0, max2)
  224. }
  225. obj[`${attr}`] = num
  226. } else if (type === 'NULL_XX') {
  227. obj[`${attr}`] = obj[`${attr}`]
  228. .replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g, '')
  229. .replace(/\s/g, '')
  230. .substring(0, max1)
  231. }
  232. },
  233. /**
  234. * 合计
  235. * @param param 合计参数
  236. * @param array 自定义数组 eg: [ { prop: amount, scopeType: 1 } ]
  237. * @returns
  238. */
  239. getSummaries(param, array) {
  240. const { columns, data } = param
  241. const sums = []
  242. columns.forEach((column, index) => {
  243. if (index === 0) {
  244. sums[index] = '合计'
  245. return
  246. }
  247. array.forEach(item => {
  248. if (item.prop === column.property) {
  249. const values = data.map(item => Number(item[column.property]))
  250. sums[index] = values.reduce((prev, curr) => {
  251. const value = Number(curr)
  252. if (!isNaN(value)) {
  253. return prev + curr
  254. } else {
  255. return prev
  256. }
  257. }, 0)
  258. if (item.scopeType) {
  259. switch (item.scopeType) {
  260. case 3:
  261. return (sums[index] = this.numberFormat(sums[index], 2, true));
  262. case 4:
  263. return (sums[index] = this.numberFormat(sums[index], 2, false));
  264. case 5:
  265. return (sums[index] = this.numberFormat(sums[index], 0, false));
  266. case 6:
  267. return (sums[index] = this.numberFormat(sums[index], 4, false));
  268. case 7:
  269. return (sums[index] = this.numberFormat(sums[index], 6, false));
  270. case 8:
  271. return (sums[index] = this.numberFormat(sums[index], 3, false));
  272. case 9:
  273. return (sums[index] = this.numberFormat(sums[index], 2, true, undefined, true));
  274. }
  275. }
  276. }
  277. })
  278. })
  279. return sums
  280. },
  281. /**
  282. * 加载
  283. */
  284. loading(ref) {
  285. ref.loadingInstance = ref.$loading({
  286. fullscreen: true,
  287. text: '请稍后'
  288. })
  289. },
  290. /**
  291. * 取消加载
  292. */
  293. loadingHidden(ref) {
  294. if (ref.loadingInstance != null) {
  295. setTimeout(() => {
  296. ref.loadingInstance.close()
  297. }, 500);
  298. }
  299. },
  300. /**
  301. * 日期转字符串
  302. */
  303. dateToStr(date = new Date(), format = 'YYYY-MM-DD') {
  304. return moment(date).format(format);
  305. },
  306. /**
  307. * @description 导出EXCEL
  308. * @param ref 组件
  309. * @param methodsName 查询方法
  310. * @param params 查询参数
  311. * @param name EXCEL名称
  312. */
  313. zDownloadExcel(ref, methodsName, params, name) {
  314. let req = this.zDeleteObjectInvalidKey(params, true)
  315. methodsName.get(req)
  316. .then(res => {
  317. let url = window.URL.createObjectURL(new Blob([res], { type: 'application/vnd.ms-excel;charset=UTF-8' }))
  318. let link = document.createElement('a')
  319. link.href = url
  320. link.download = name
  321. document.body.appendChild(link)
  322. link.click()
  323. document.body.removeChild(link)
  324. window.URL.revokeObjectURL(url)
  325. })
  326. .catch()
  327. .finally(() => {
  328. // ref.endLoading()
  329. })
  330. },
  331. /**
  332. * @description 删除Object对象无效key
  333. * @param object1 对象
  334. * @param delEmptyStr 是否删除值为''的key
  335. * @return 删除后的对象
  336. */
  337. zDeleteObjectInvalidKey(object1 = {}, delEmptyStr = false) {
  338. let object = _.cloneDeep(object1)
  339. for (let key in object) {
  340. if (object[key] === undefined || object[key] === null) {
  341. delete object[key]
  342. }
  343. if (delEmptyStr && object[key] === '') {
  344. delete object[key]
  345. }
  346. }
  347. return object
  348. },
  349. /**
  350. * @description 导出模板
  351. * @param name 模板名称
  352. */
  353. zDownloadTemplate(name) {
  354. let a = document.createElement('a')
  355. a.href = '/static/'.concat(name)
  356. console.log(a.href);
  357. a.download = name
  358. a.click()
  359. },
  360. /**
  361. * @description 导入EXCEL
  362. * @param ref 组件
  363. * @param methodsName 导入方法
  364. * @param data 数据
  365. */
  366. zImportExcel(ref, methodsName, data) {
  367. this.loading(ref);
  368. methodsName.post(data)
  369. .then(res => {
  370. if (res.message === 'success') {
  371. if (!res.errorMsg) {
  372. ref.$message.success('批量导入成功')
  373. ref.searchFun()
  374. } else {
  375. ref.$message.error(res.errorMsg, true)
  376. }
  377. }
  378. }).catch(()=>{}).finally(() => {
  379. this.loadingHidden(ref);
  380. })
  381. },
  382. /**
  383. * @description 处理cascader
  384. * @param params 参数对象
  385. * @param cascaderNames cascader属性名称
  386. * @return object 处理后的参数对象
  387. */
  388. handleCascader(params = {}, ...cascaderNames) {
  389. let object = _.cloneDeep(params)
  390. for (let i = 0; i < cascaderNames.length; i++) {
  391. let cascaderName = cascaderNames[i]
  392. if (object[`${cascaderName}`] && typeof object[`${cascaderName}`] !== 'string') {
  393. object[`${cascaderName}`] =
  394. object[`${cascaderName}`].length > 0 ? object[`${cascaderName}`][object[`${cascaderName}`].length - 1] : null
  395. }
  396. }
  397. return object
  398. },
  399. addWatermark(file,name) {
  400. return new Promise((resolve, reject) => {
  401. const canvas = document.createElement('canvas');
  402. const ctx = canvas.getContext('2d');
  403. const image = new Image();
  404. image.onload = function () {
  405. // 设置canvas大小为图片大小
  406. canvas.width = image.width;
  407. canvas.height = image.height;
  408. // 在canvas上绘制原始图片
  409. ctx.drawImage(image, 0, 0);
  410. // 添加水印文本
  411. let size = Math.ceil(image.width*image.height /100000);
  412. ctx.font = `${size}px Arial`;
  413. ctx.fillStyle = 'rgb(228,233,229)';
  414. const watermarkText = name; // 水印文本
  415. const angle = 15 * Math.PI / 180; // 将15度转换为弧度
  416. // 计算每个部分的高度
  417. const partHeight = canvas.height / 3;
  418. // 计算每行的宽度
  419. const rowWidth = canvas.width / Math.ceil(canvas.width / canvas.height);
  420. // 遍历每行
  421. for (let i = 0; i < 3; i++) {
  422. // 计算水印的y坐标
  423. const y = (i + 0.2) * partHeight; // 在每行的中间位置添加水印
  424. // 遍历每个部分
  425. for (let j = 0; j < Math.ceil(canvas.width / rowWidth); j++) {
  426. // 计算水印的x坐标
  427. const x = (j + 0.2) * rowWidth; // 在每个部分的中间位置添加水印
  428. // 保存当前绘图环境状态
  429. ctx.save();
  430. // 将坐标系移动到水印的位置
  431. ctx.translate(x, y);
  432. // 应用倾斜角度
  433. ctx.rotate(angle); // 将角度转换为弧度
  434. // 绘制水印文本
  435. ctx.fillText(watermarkText, 0, 0);
  436. // 恢复绘图环境到之前的状态
  437. ctx.restore();
  438. }
  439. }
  440. // const watermarkCount = 3; // 水印数量
  441. // const horizontalSpacing = canvas.width / (watermarkCount+2 ); // 水平间距
  442. // const verticalSpacing = canvas.height / (watermarkCount +2); // 垂直间距
  443. // for (let i = 1; i <= watermarkCount; i++) {
  444. // for (let j = 1; j <= watermarkCount; j++) {
  445. // const x = i * horizontalSpacing;
  446. // const y = j * verticalSpacing;
  447. // ctx.save(); // 保存当前的绘图状态
  448. // ctx.translate(x, y); // 将原点移动到水印位置
  449. // ctx.rotate(angle); // 固定倾斜15度
  450. // ctx.fillText(watermarkText, 0, 0); // 绘制倾斜的水印文本
  451. // ctx.restore(); // 恢复之前保存的绘图状态,避免影响后续绘图
  452. // }
  453. // }
  454. // const watermarkText = '社保信息'; // 水印文本
  455. // const watermarkCountX = Math.ceil(canvas.width / 150); // 根据图片宽度计算水印横向数量
  456. // const watermarkCountY = Math.ceil(canvas.height / 150); // 根据图片高度计算水印纵向数量
  457. // const angle = 10 * Math.PI / 180; // 将5度转换为弧度
  458. // for (let i = 1; i <= watermarkCountX; i++) {
  459. // for (let j = 1; j <= watermarkCountY; j++) {
  460. // const x = i * (canvas.width / (watermarkCountX + 1));
  461. // const y = j * (canvas.height / (watermarkCountY + 1));
  462. // ctx.save(); // 保存当前的绘图状态
  463. // ctx.translate(x, y); // 将原点移动到水印位置
  464. // ctx.rotate(angle); // 固定倾斜5度
  465. // ctx.fillText(watermarkText, 0, 0); // 绘制倾斜的水印文本
  466. // ctx.restore(); // 恢复之前保存的绘图状态,避免影响后续绘图
  467. // }
  468. // }
  469. function _dataURLtoBlob(dataurl) {
  470. var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
  471. bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  472. while(n--){
  473. u8arr[n] = bstr.charCodeAt(n);
  474. }
  475. return new Blob([u8arr], {type:mime});
  476. }
  477. const dataurl2 = canvas.toDataURL('image/jpeg', 0.7);
  478. const blob = _dataURLtoBlob(dataurl2)
  479. return resolve(new File([blob], file.name, { type: 'image/jpg' }));
  480. };
  481. image.onerror = function () {
  482. reject(new Error('Failed to load image'));
  483. };
  484. // 使用异步方法加载图片
  485. image.src = URL.createObjectURL(file);
  486. });
  487. },
  488. /**
  489. * @description 处理多选cascader
  490. * @param params 参数对象
  491. * @param cascaderNames cascader属性名称
  492. * @return object 处理后的参数对象
  493. */
  494. handleMultipleCascader(params = {}, ...cascaderNames) {
  495. let object = _.cloneDeep(params)
  496. for (let i = 0; i < cascaderNames.length; i++) {
  497. let cascaderName = cascaderNames[i]
  498. if (object[`${cascaderName}`]) {
  499. for (let i = 0; i < object[`${cascaderName}`].length; i++) {
  500. if (typeof object[`${cascaderName}`][i] !== 'string') {
  501. object[`${cascaderName}`][i] =
  502. object[`${cascaderName}`][i].length > 0 ? object[`${cascaderName}`][i][object[`${cascaderName}`][i].length - 1] : null
  503. }
  504. }
  505. }
  506. }
  507. return object
  508. }
  509. };
  510. /**
  511. * @description 双子函数 解析名称字符串 与getAttrByStrName共同使用 对象[`${属性}`] 达到给组件属性赋值作用
  512. * @param ref 组件
  513. * @param name 组件属性名
  514. */
  515. function getObjByStrName(ref, name) {
  516. let obj = ref
  517. let array = name.split(/\.|\[/)
  518. for (let i = 0; i < array.length - 1; i++) {
  519. obj = array[i].endsWith(']') ? obj[parseInt(array[i].substring(0, array[i].length - 1))] : obj[`${array[i]}`]
  520. }
  521. return obj
  522. }
  523. /**
  524. * @description 双子函数 解析名称字符串 与getObjByStrName共同使用 对象[`${属性}`] 达到给组件属性赋值作用
  525. * @param ref 组件
  526. * @param name 组件属性名
  527. */
  528. function getAttrByStrName(ref, name) {
  529. let array = name.split(/\.|\[/)
  530. return array[array.length - 1]
  531. }
  532. export default zTool