index.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /**
  2. * 复制文本到剪贴板
  3. * @param {string} text 需要复制的文本
  4. * @returns {Promise<boolean>} 是否复制成功
  5. */
  6. export function copyToClipboard(text) {
  7. // 现代浏览器方案 (推荐)
  8. if (navigator.clipboard && window.isSecureContext) {
  9. return navigator.clipboard.writeText(text)
  10. .then(() => true)
  11. .catch(() => false);
  12. }
  13. // 传统浏览器兼容方案
  14. return new Promise((resolve) => {
  15. try {
  16. // 创建临时文本域
  17. const textarea = document.createElement('textarea');
  18. textarea.value = text;
  19. textarea.style.position = 'fixed'; // 防止滚动
  20. textarea.style.opacity = '0'; // 透明化
  21. document.body.appendChild(textarea);
  22. // 选中内容 (兼容iOS)
  23. if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
  24. const range = document.createRange();
  25. range.selectNodeContents(textarea);
  26. const selection = window.getSelection();
  27. selection.removeAllRanges();
  28. selection.addRange(range);
  29. textarea.setSelectionRange(0, 999999);
  30. } else {
  31. textarea.select();
  32. }
  33. // 执行复制命令
  34. const success = document.execCommand('copy');
  35. document.body.removeChild(textarea);
  36. resolve(success);
  37. } catch (err) {
  38. resolve(false);
  39. }
  40. });
  41. }
  42. export async function copy(value) {
  43. const success = await copyToClipboard(value);
  44. if (success) {
  45. uni.showToast({ //提示
  46. title: '复制成功'
  47. })
  48. } else {
  49. uni.showToast({ //提示
  50. title: '复制失败',
  51. })
  52. }
  53. }
  54. /**
  55. * 通用数据脱敏函数
  56. * @param {string} str 原始字符串
  57. * @param {number} start 脱敏起始位置(从0开始)
  58. * @param {number} end 脱敏结束位置(不包含)
  59. * @param {string} mask 替换字符(默认*)
  60. * @returns {string} 脱敏后的字符串
  61. */
  62. export function dataMasking(str, start = 0, end = 0, mask = '*') {
  63. if (!str || typeof str !== 'string') return str
  64. if (start >= end) return str
  65. const head = str.slice(0, start)
  66. const tail = str.slice(end)
  67. const masked = mask.repeat(end - start)
  68. return head + masked + tail
  69. }
  70. // 常用预定义规则
  71. export const MaskRules = {
  72. // 手机号脱敏(显示前3后4)
  73. mobile: (str) => dataMasking(str, 3, 7),
  74. // 身份证号脱敏(显示前6后4)
  75. idCard: (str) => {
  76. if (str.length === 18) return dataMasking(str, 6, 14)
  77. if (str.length === 15) return dataMasking(str, 6, 12)
  78. return str
  79. },
  80. // 银行卡号脱敏(显示前6后4)
  81. bankCard: (str) => dataMasking(str, 6, str.length - 4),
  82. // 姓名脱敏(张三 → 张*,欧阳修 → 欧**)
  83. name: (str) => {
  84. if (str.length <= 1) return str
  85. return dataMasking(str, 1, str.length - 1)
  86. }
  87. }
  88. // 使用示例
  89. const sensitiveData = {
  90. mobile: '13812345678',
  91. idCard: '110101199003077654',
  92. bankCard: '6225880136745321',
  93. name: '张三丰'
  94. }
  95. // 脱敏处理
  96. const maskedData = {
  97. mobile: MaskRules.mobile(sensitiveData.mobile), // 138****5678
  98. idCard: MaskRules.idCard(sensitiveData.idCard), // 110101********7654
  99. bankCard: MaskRules.bankCard(sensitiveData.bankCard), // 622588******5321
  100. name: MaskRules.name(sensitiveData.name) // 张*丰
  101. }
  102. /**
  103. * 对象转查询字符串
  104. * @param {object} params - 输入对象
  105. * @param {boolean} [encode=true] - 是否进行URL编码
  106. * @returns {string} 转换后的字符串
  107. */
  108. export function toQueryString(params, encode = true) {
  109. const processValue = (value) => {
  110. if (value === null || value === undefined) return ''
  111. if (Array.isArray(value)) return value.join(',')
  112. if (typeof value === 'object') return JSON.stringify(value)
  113. return String(value)
  114. }
  115. return Object.entries(params)
  116. .filter(([_, value]) => value !== null && value !== undefined)
  117. .map(([key, value]) => {
  118. const processed = processValue(value)
  119. return encode ?
  120. `${encodeURIComponent(key)}=${encodeURIComponent(processed)}` :
  121. `${key}=${processed}`
  122. })
  123. .join('&')
  124. }
  125. /**
  126. * 构造树型结构数据
  127. * @param {*} data 数据源
  128. * @param {*} id id字段 默认 'id'
  129. * @param {*} parentId 父节点字段 默认 'parentId'
  130. * @param {*} children 孩子节点字段 默认 'children'
  131. * @param {*} rootId 根Id 默认 0
  132. */
  133. export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children', rootId = 0) {
  134. rootId = rootId || Math.min.apply(Math, data.map(item => {
  135. return item[parentId]
  136. })) || 0
  137. //对源数据深度克隆
  138. const cloneData = JSON.parse(JSON.stringify(data))
  139. //循环所有项
  140. const treeData = cloneData.filter(father => {
  141. let branchArr = cloneData.filter(child => {
  142. //返回每一项的子级数组
  143. return father[id] === child[parentId]
  144. });
  145. branchArr.length > 0 ? father.children = branchArr : '';
  146. //返回第一层
  147. return father[parentId] === rootId;
  148. });
  149. return treeData !== '' ? treeData : data;
  150. }
  151. export function reNameKeys(data=[]) {
  152. return data.map(item => {
  153. const newItem = {
  154. name: item.label,
  155. id: item.value,
  156. type: item.children ? 0 : 1
  157. };
  158. if (item.children && Array.isArray(item.children)) {
  159. newItem.children = reNameKeys(item.children);
  160. }
  161. return newItem;
  162. });
  163. }
  164. export function reTextKeys(data = []) {
  165. return data.map(item => {
  166. const newItem = {
  167. text: item.label,
  168. value: item.value,
  169. type: item.children ? 0 : 1,
  170. selected: item.selected || false,
  171. };
  172. if (item.children && Array.isArray(item.children)) {
  173. newItem.children = reTextKeys(item.children);
  174. }
  175. return newItem;
  176. });
  177. }