useEdit.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import { routeName, router } from '@/router'
  2. import { Dialog } from '@kankan/components/index'
  3. import { ref, Ref, watch } from 'vue'
  4. import { useAsyncBus, Handler } from './useAsyncBus'
  5. import { useViewStack } from './useViewStack'
  6. import { disabledMap } from './custom'
  7. import { ui18n } from '@/lang'
  8. import { useConfirm } from '@/hook'
  9. export const editBus = useAsyncBus<{ save: void; leave: void }>()
  10. export type EditHandler = () => void
  11. export type EditStatus = {
  12. edit: EditHandler
  13. desave: EditHandler
  14. leaveSave: EditHandler
  15. leave: EditHandler
  16. }
  17. let mark: EditStatus & { isedit: boolean; isdesave: boolean }
  18. export type KV<T> = [Ref<T>, T] | [Ref<T>, T, T]
  19. export type KVS<T> = Array<KV<T>>
  20. export type VS<T> = Array<KVS<T>[number][1]>
  21. // 进入编辑界面
  22. export const useEdit = <T>(args?: KV<T> | KVS<T>) => {
  23. if (!args) return mark.edit()
  24. const kvs = (Array.isArray(args[0]) ? args : [args]) as KVS<T>
  25. const cacheks = kvs.map(([k, _, d = k.value]) => d)
  26. const updateKs = (value: VS<T>) => {
  27. for (let i = 0; i < kvs.length; i++) {
  28. kvs[i][0].value = value[i]
  29. }
  30. }
  31. mark.edit()
  32. updateKs(kvs.map(([_, v]) => v))
  33. editBus.on('leave', () => updateKs(cacheks), { last: true })
  34. }
  35. // 保存
  36. export const useEmitSave = () => {
  37. editBus.emit('save').then(() => {
  38. if (!disabledMap.autoLeave) {
  39. useLeaveEdit()
  40. } else {
  41. useLeaveSave()
  42. }
  43. })
  44. }
  45. // 退出
  46. let sureBackPage = true
  47. export const useEmitLeave = async (isBack = true) => {
  48. if (mark.isdesave) {
  49. const isLeave = await useConfirm(ui18n.t('sys.forceLeaveConfirm'))
  50. if (!isLeave) {
  51. return false
  52. }
  53. }
  54. sureBackPage = isBack
  55. editBus.on('leave', () => (sureBackPage = true), { last: true })
  56. editBus.emit('leave').then(useLeaveEdit)
  57. return true
  58. }
  59. export const useLeaveEditRaw = () => mark.leave()
  60. // 退出保存
  61. export const useLeaveSave = () => {
  62. editBus.off('save')
  63. mark.leaveSave()
  64. }
  65. // 退出编辑
  66. export const useLeaveEdit = () => {
  67. editBus.off('save')
  68. editBus.off('leave')
  69. mark.leave()
  70. mark.leaveSave()
  71. }
  72. export type DesaveArgs = {
  73. save?: Handler
  74. leave?: Handler
  75. }
  76. // 进入未保存模式
  77. export const useDesave = (cbs: DesaveArgs = {}) => {
  78. mark.desave()
  79. cbs.leave && editBus.on('leave', cbs.leave)
  80. cbs.save && editBus.on('save', cbs.save)
  81. }
  82. export type DesaveArgsAssistArgs<T> = DesaveArgs & {
  83. intercept?: (newCurrent: T, oldCurrent: T) => boolean
  84. auto?: boolean
  85. backup?: () => void
  86. recovery?: () => void
  87. }
  88. export function useDesaveAssist<T extends object>(
  89. current: T,
  90. setting: DesaveArgsAssistArgs<T> & { auto: false }
  91. ): () => () => void
  92. export function useDesaveAssist<T extends object>(
  93. current: T,
  94. setting: DesaveArgsAssistArgs<T> & { auto: true }
  95. ): { desave: Ref<boolean> }
  96. export function useDesaveAssist<T extends object>(
  97. current: T,
  98. setting: DesaveArgsAssistArgs<T>
  99. ) {
  100. const desave = ref(false)
  101. const { recovery, backup, leave } = setting
  102. const leaveHandler = () => {
  103. isSave = false;
  104. if (recovery || backup) {
  105. recovery && recovery()
  106. backup && backup()
  107. desave.value = false
  108. leave && (leave as any)()
  109. }
  110. }
  111. let isSave = false
  112. const save = async args => {
  113. isSave = true
  114. try {
  115. await setting.save(args)
  116. } catch (e) {
  117. // recovery && recovery()
  118. throw e
  119. }
  120. isSave = false
  121. }
  122. const handler = (newv, oldv) => {
  123. if (!isSave) {
  124. if (!setting.intercept || setting.intercept(newv, oldv)) {
  125. desave.value = true
  126. mark.isedit || useEdit()
  127. useDesave({ save })
  128. backup && editBus.on('save', backup, { last: true })
  129. }
  130. leaveHandler && editBus.on('leave', leaveHandler, { last: true })
  131. }
  132. }
  133. const saveWatch = () => {
  134. backup && backup()
  135. return watch(current, handler, { deep: true })
  136. }
  137. if (setting.auto) {
  138. useViewStack(saveWatch)
  139. return { desave }
  140. } else {
  141. return saveWatch
  142. }
  143. }
  144. export const useBack = (type: 'save' | 'leave' = 'leave') => {
  145. if (sureBackPage && (type === 'leave' || !disabledMap.autoLeave)) {
  146. if (!history.state.back) {
  147. router.replace({ name: routeName.value.query })
  148. } else {
  149. router.back()
  150. }
  151. }
  152. }
  153. type HookArgs = (...args: any) => void | (() => any | void)
  154. export const useViewEdit = (
  155. hook?: HookArgs,
  156. cb: (type: 'save' | 'leave') => void = useBack
  157. ) =>
  158. useViewStack(() => {
  159. useEdit()
  160. editBus.on('leave', () => cb('leave'), { last: true })
  161. editBus.on('save', () => cb('save'), { last: true })
  162. const exitFn = hook && hook()
  163. return () => {
  164. useLeaveEdit()
  165. exitFn && exitFn()
  166. }
  167. })
  168. export const setupEdit = (status: EditStatus) => {
  169. mark = {
  170. edit: () => {
  171. mark.isedit = true
  172. status.edit()
  173. },
  174. desave: () => {
  175. mark.isdesave = true
  176. status.desave()
  177. },
  178. leave: () => {
  179. mark.isdesave = false
  180. mark.isedit = false
  181. status.leave()
  182. },
  183. leaveSave: () => {
  184. mark.isdesave = false
  185. status.leaveSave()
  186. },
  187. isedit: false,
  188. isdesave: false
  189. }
  190. }