vue.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. import {
  2. isRef,
  3. toRaw,
  4. UnwrapRef,
  5. computed,
  6. ref,
  7. Ref,
  8. WritableComputedRef,
  9. ComputedRef,
  10. shallowReactive,
  11. reactive,
  12. watchEffect,
  13. nextTick,
  14. watch,
  15. readonly
  16. } from 'vue'
  17. import { round, toRawType } from './index'
  18. import { local } from './store'
  19. // 复制vue源数据 防止引用修改
  20. export const blocking = <T>(data: T): UnwrapRef<T> => {
  21. const raw = toRaw(isRef(data) ? data.value : data) as any
  22. return raw
  23. // const rawType = toRawType(raw)
  24. // switch(rawType) {
  25. // case 'Array':
  26. // return recursionCopy(raw, raw)
  27. // case 'Object':
  28. // return recursionCopy(raw, raw)
  29. // default:
  30. // return data as UnwrapRef<T>
  31. // }
  32. }
  33. // 执行函数时保证参数没有其他地方引用
  34. export const callFunArgsBlocking = <T extends (...args: Array<any>) => any>(
  35. fun: T
  36. ): T =>
  37. ((...args) => {
  38. let result
  39. try {
  40. result = fun.apply(this, blocking(args))
  41. } catch (e) {
  42. console.error(e)
  43. }
  44. return result
  45. }) as T
  46. // 对象所有函数隔离传入参数
  47. const blockfcMap = new WeakMap()
  48. export const blockingFunCall = <T extends object>(data: T): T =>
  49. new Proxy(data, {
  50. get(...args) {
  51. let value = Reflect.get(...args) as any
  52. if (blockfcMap.has(value)) {
  53. return blockfcMap.get(value)
  54. }
  55. const valueType = toRawType(value)
  56. if (valueType === 'Object' || valueType === 'Array') {
  57. const blockfc = blockingFunCall(value)
  58. blockfcMap.set(value, blockfc)
  59. value = blockfc
  60. } else if (valueType === 'Function') {
  61. value = callFunArgsBlocking(value)
  62. }
  63. return value
  64. }
  65. })
  66. export type Tree = { children?: Array<any> }
  67. export const getFlatTree = <T extends Tree>(
  68. tree: T,
  69. itself: boolean = true
  70. ): Array<T> => {
  71. const options = [] as Array<T>
  72. if (itself) {
  73. options.push(tree)
  74. }
  75. if (tree.children && tree.children.length > 0) {
  76. for (const item of tree.children) {
  77. options.push(...(getFlatTree(item, itself) as Array<T>))
  78. }
  79. } else if (!itself) {
  80. options.push(tree)
  81. }
  82. return options
  83. }
  84. export type LinkageTree<T> = {
  85. select: WritableComputedRef<boolean>
  86. selects: Ref<Array<T>>
  87. children: Array<LinkageTree<T>>
  88. options: Array<T>
  89. current: T
  90. }
  91. // 生成树联动
  92. export const linkageSelectTree = <T extends Tree>(
  93. tree: T,
  94. itself: boolean = true,
  95. defSelect?: boolean,
  96. selects = ref([]) as Ref<Array<T>>
  97. ): LinkageTree<T> => {
  98. const alloptions = getFlatTree(tree, itself)
  99. const childrenCheck = tree.children
  100. ? tree.children.map(item => linkageSelectTree(item, itself, false, selects))
  101. : []
  102. const changeCheck = (check: boolean) => {
  103. alloptions.forEach(item => {
  104. let index = selects.value.indexOf(item)
  105. if (!~index) {
  106. index = selects.value.indexOf(reactive(item as any))
  107. }
  108. if (~index && !check) {
  109. selects.value.splice(index, 1)
  110. // selects.value = [...selects.value]
  111. } else if (!~index && check) {
  112. selects.value.push(item)
  113. // selects.value = [...selects.value]
  114. }
  115. })
  116. }
  117. let cache: boolean
  118. const allCheck = computed({
  119. get: () => {
  120. let current = true
  121. for (const option of alloptions) {
  122. let i = 0
  123. for (; i < selects.value.length; i++) {
  124. const select = toRaw(selects.value[i])
  125. if (toRaw(option) === select) {
  126. break
  127. }
  128. }
  129. if (i === selects.value.length) {
  130. current = false
  131. break
  132. }
  133. }
  134. cache = current
  135. return current
  136. },
  137. set: check => {
  138. changeCheck(check)
  139. }
  140. })
  141. if (defSelect) {
  142. nextTick(() => (allCheck.value = defSelect))
  143. }
  144. return {
  145. current: tree,
  146. select: allCheck,
  147. children: childrenCheck,
  148. selects,
  149. options: alloptions
  150. }
  151. }
  152. export type SimleTree<T> = Omit<
  153. LinkageTree<T>,
  154. 'select' | 'selects' | 'children'
  155. > & { children: Array<SimleTree<T>> }
  156. //获取选择树路径
  157. export const getLinkageTreeLocal = <T extends object>(
  158. tree: SimleTree<T>,
  159. select: T
  160. ) => {
  161. if (toRaw(tree) === toRaw(select)) {
  162. return [select]
  163. } else if (tree.children) {
  164. for (const item of tree.children) {
  165. const locals = getLinkageTreeLocal(item, select)
  166. if (locals.length) {
  167. return [tree, ...locals]
  168. }
  169. }
  170. }
  171. return []
  172. }
  173. export const getLinkageOptionLocal = <T extends object>(
  174. tree: SimleTree<T>,
  175. option: T
  176. ): Array<T> => {
  177. if (tree.options.includes(option)) {
  178. for (const item of tree.children) {
  179. const locals = getLinkageOptionLocal(item, option)
  180. if (locals.length) {
  181. return [tree.current, ...locals]
  182. }
  183. }
  184. return [tree.current]
  185. }
  186. return []
  187. }
  188. export type Stack<T> = {
  189. push: (raw: T) => void
  190. pop: () => T
  191. length: ComputedRef<number>
  192. current: ComputedRef<T>
  193. }
  194. // 栈构建
  195. export const stackFactory = <T>(initVal?: T, debug?: boolean): Stack<T> => {
  196. const stack = shallowReactive([]) as Array<T>
  197. if (initVal !== void 0) {
  198. stack.push(initVal)
  199. }
  200. return {
  201. push(raw: T) {
  202. stack.push(raw)
  203. },
  204. pop() {
  205. let ret = stack[stack.length-- - 1]
  206. return ret
  207. },
  208. current: computed(() => {
  209. return stack[stack.length - 1]
  210. }),
  211. length: computed(() => stack.length)
  212. }
  213. }
  214. export const fastStacksValue = <T extends { [key in any]: Stack<any> }>(
  215. stacks: T
  216. ) => {
  217. const result = {} as {
  218. [key in keyof T]: UnwrapRef<T[key]['current']['value']>
  219. }
  220. const keys = Object.keys(stacks) as Array<keyof T>
  221. const proxy = new Proxy(result, {
  222. get(_, key: keyof T) {
  223. if (keys.includes(key)) {
  224. return isRef(stacks[key].current.value)
  225. ? (stacks[key].current.value as any).value
  226. : stacks[key].current.value
  227. } else {
  228. return stacks[key]
  229. }
  230. },
  231. set(_, key: keyof T, val) {
  232. if (isRef(stacks[key].current.value)) {
  233. ;(stacks[key].current.value as any).value = val
  234. return true
  235. } else {
  236. return false
  237. }
  238. }
  239. })
  240. return proxy
  241. }
  242. export type MarkStack<T, K> = {
  243. push(raw: K, key: T): ReturnType<Stack<K>['push']>
  244. pop(key: T): ReturnType<Stack<K>['pop']>
  245. get(key: T): Stack<K>['current']
  246. }
  247. // 标识栈构建
  248. export const markStackFactory = <T extends object, K>() => {
  249. const markMap = shallowReactive(new Map<T, Stack<K>>())
  250. return {
  251. push(raw: K, key: T) {
  252. if (!markMap.has(key)) {
  253. markMap.set(key, stackFactory<K>())
  254. }
  255. const stack = markMap.get(key)
  256. stack.push(raw)
  257. },
  258. pop(key: T) {
  259. const stack = markMap.get(key)
  260. if (stack) {
  261. const ret = stack.pop()
  262. if (!stack.length.value) {
  263. markMap.delete(key)
  264. }
  265. return ret
  266. }
  267. },
  268. get(key: T) {
  269. const stack = markMap.get(key)
  270. if (stack) {
  271. return stack.current
  272. }
  273. }
  274. }
  275. }
  276. export type ManageStack<T, K> = {
  277. push(raw: K, key?: T): ReturnType<Stack<K>['push']>
  278. pop(key?: T): ReturnType<Stack<K>['pop']>
  279. get(key?: T): Stack<K>['current']
  280. }
  281. export const mangeStackFactory = <T extends object, K>() => {
  282. const stack = stackFactory<K>()
  283. const markStack = markStackFactory<T, K>()
  284. return {
  285. push: (raw: K, key?: T) =>
  286. key ? markStack.push(raw, key) : stack.push(raw),
  287. pop: (key: T) => {
  288. return key ? markStack.pop(key) : stack.pop()
  289. },
  290. get: (key: T) => {
  291. return key && markStack.get(key) ? markStack.get(key) : stack.current
  292. }
  293. }
  294. }
  295. export const watchGroupChange = <T>(
  296. origin: Ref<Array<T>> | Array<T>,
  297. addCallback: (atom: T) => void,
  298. removeCallback?: (atom: T) => void
  299. ) => {
  300. watch(
  301. origin,
  302. (ctrls: Array<T>, oldCtrls: Array<T>) => {
  303. if (oldCtrls && removeCallback) {
  304. oldCtrls.filter(item => !ctrls.includes(item)).forEach(removeCallback)
  305. }
  306. ;(oldCtrls
  307. ? ctrls.filter(item => !oldCtrls.includes(item))
  308. : ctrls
  309. ).forEach(addCallback)
  310. },
  311. { immediate: true }
  312. )
  313. }
  314. export const genCountDown = (
  315. localKey: string,
  316. mis: number,
  317. preTime?: number
  318. ) => {
  319. if (!preTime) {
  320. preTime = Number(local.get(localKey)) || 0
  321. }
  322. const countmis = ref(0)
  323. let time
  324. const update = () => {
  325. const now = Date.now()
  326. const downmis = round((now - preTime) / 1000, 0)
  327. if (downmis < mis) {
  328. countmis.value = mis - downmis
  329. time = setTimeout(update, 1000)
  330. } else {
  331. countmis.value = 0
  332. local.del(localKey)
  333. }
  334. }
  335. local.set(localKey, preTime.toString())
  336. update()
  337. return {
  338. count: countmis,
  339. update: (time: number) => {
  340. clearTimeout(time)
  341. preTime = time
  342. local.set(localKey, preTime.toString())
  343. update()
  344. }
  345. }
  346. }
  347. function isTabletf() {
  348. // 获取设备的屏幕宽度和高度
  349. var screenWidth = window.screen.width;
  350. var screenHeight = window.screen.height;
  351. // 计算屏幕的对角线长度
  352. var diagonalSize = Math.sqrt(Math.pow(screenWidth, 2) + Math.pow(screenHeight, 2));
  353. // 判断屏幕的对角线长度是否大于等于7英寸(一般认为大于等于7英寸的设备是平板)
  354. if (diagonalSize >= 7) {
  355. return true;
  356. } else {
  357. return false;
  358. }
  359. }
  360. function isPCf() {
  361. const ua = navigator.userAgent.toLowerCase();
  362. const agents = ["android", "iphone", "symbianos", "windows phone", "ipad", "ipod"];
  363. for (let i = 0; i < agents.length; i++) {
  364. if (ua.indexOf(agents[i]) !== -1) {
  365. console.error(agents[i])
  366. return false;
  367. }
  368. }
  369. return true;
  370. }
  371. export const os = (function () {
  372. let ua = navigator.userAgent.toLowerCase();
  373. let isWindowsPhone = /(?:windows phone)/.test(ua)
  374. let isSymbian = /(?:symbianOS)/.test(ua) || isWindowsPhone
  375. let isAndroid = /(?:android)/.test(ua)
  376. let isFireFox = /(?:firefox)/.test(ua)
  377. let isChrome = /(?:chrome|crios)/.test(ua)
  378. let isTablet = isTabletf()
  379. let isPhone = /(?:iphone)/.test(ua) && !isTablet
  380. // let isPc = !isPhone && !isAndroid && !isSymbian && !isTablet
  381. let isPc = isPCf()
  382. let isWX = /(?:microMessenger)/.test(ua)
  383. // if (isPc && navigator.maxTouchPoints > 1) {
  384. // isTablet = true
  385. // }
  386. const isHorizontal = ref(false)
  387. const getHorizotal = () =>
  388. window.innerWidth > window.innerHeight && window.innerWidth - 80 > 320
  389. let timeout
  390. if (!isPc) {
  391. const changHorizontal = () => {
  392. isHorizontal.value = getHorizotal()
  393. clearTimeout(timeout)
  394. timeout = setTimeout(() => {
  395. isHorizontal.value = getHorizotal()
  396. }, 300)
  397. }
  398. window.addEventListener('resize', changHorizontal)
  399. changHorizontal()
  400. }
  401. return {
  402. isTablet: isTablet,
  403. isPhone: isPhone,
  404. isAndroid: isAndroid,
  405. isPc: isPc,
  406. isWX: isWX,
  407. isHorizontal
  408. }
  409. })()