index.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <template>
  2. <div ref="container">
  3. <a-modal
  4. ref="modal"
  5. :class="getClass(modalClass)"
  6. :style="getStyle(modalStyle)"
  7. :visible="visible"
  8. :getContainer="() => $refs.container"
  9. :wrapClassName="wrapClassNameInfo()"
  10. :mask="isDesktop()"
  11. :maskClosable="false"
  12. v-bind="_attrs"
  13. v-on="$listeners"
  14. @ok="handleOk"
  15. @cancel="handleCancel"
  16. >
  17. <slot></slot>
  18. <template v-if="!isNoTitle" slot="title">
  19. <a-row class="j-modal-title-row" type="flex">
  20. <a-col class="left">
  21. <slot name="title">{{ title }}</slot>
  22. </a-col>
  23. <a-col class="right">
  24. <a-tooltip title="新手引导">
  25. <a-button
  26. v-if="switchHelp"
  27. @click="handleHelp"
  28. style="right: 112px"
  29. class="ant-modal-close ant-modal-close-x"
  30. ghost
  31. type="link"
  32. icon="question-circle"
  33. />
  34. </a-tooltip>
  35. <a-button
  36. v-if="switchFullscreen"
  37. @click="toggleFullscreen"
  38. class="ant-modal-close ant-modal-close-x"
  39. ghost
  40. type="link"
  41. :icon="fullscreenButtonIcon"
  42. />
  43. </a-col>
  44. </a-row>
  45. </template>
  46. <!-- 处理 scopedSlots -->
  47. <template v-for="slotName of scopedSlotsKeys" :slot="slotName">
  48. <slot :name="slotName"></slot>
  49. </template>
  50. <!-- 处理 slots -->
  51. <template v-for="slotName of slotsKeys" v-slot:[slotName]>
  52. <slot :name="slotName"></slot>
  53. </template>
  54. </a-modal>
  55. </div>
  56. </template>
  57. <script>
  58. import { getClass, getStyle } from '@/utils/props-util'
  59. import { triggerWindowResizeEvent, handleIntroJs } from '@/utils/util'
  60. import { mixinDevice } from '@/utils/mixin'
  61. import Vue from 'vue'
  62. export default {
  63. name: 'JModal',
  64. mixins: [mixinDevice],
  65. props: {
  66. title: String,
  67. // 可使用 .sync 修饰符
  68. visible: Boolean,
  69. // 前缀代号
  70. prefixNo: String,
  71. // 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符
  72. fullscreen: {
  73. type: Boolean,
  74. default: false,
  75. },
  76. // 是否允许展示新手引导(允许后右上角会出现一个按钮)
  77. switchHelp: {
  78. type: Boolean,
  79. default: false,
  80. },
  81. // 是否允许切换全屏(允许后右上角会出现一个按钮)
  82. switchFullscreen: {
  83. type: Boolean,
  84. default: false,
  85. },
  86. // 点击确定按钮的时候是否关闭弹窗
  87. okClose: {
  88. type: Boolean,
  89. default: true,
  90. },
  91. },
  92. data() {
  93. return {
  94. // 内部使用的 slots ,不再处理
  95. usedSlots: ['title'],
  96. // 实际控制是否全屏的参数
  97. innerFullscreen: this.fullscreen,
  98. }
  99. },
  100. computed: {
  101. // 一些未处理的参数或特殊处理的参数绑定到 a-modal 上
  102. _attrs() {
  103. let attrs = { ...this.$attrs }
  104. // 如果全屏就将宽度设为 100%
  105. if (this.innerFullscreen) {
  106. attrs['width'] = '100%'
  107. }
  108. return attrs
  109. },
  110. modalClass() {
  111. return {
  112. 'j-modal-box': true,
  113. fullscreen: this.innerFullscreen,
  114. 'no-title': this.isNoTitle,
  115. 'no-footer': this.isNoFooter,
  116. }
  117. },
  118. modalStyle() {
  119. let style = {}
  120. // 如果全屏就将top设为 0
  121. if (this.innerFullscreen) {
  122. style['top'] = '0'
  123. }
  124. return style
  125. },
  126. isNoTitle() {
  127. return !this.title && !this.allSlotsKeys.includes('title')
  128. },
  129. isNoFooter() {
  130. return this._attrs['footer'] === null
  131. },
  132. slotsKeys() {
  133. return Object.keys(this.$slots).filter((key) => !this.usedSlots.includes(key))
  134. },
  135. scopedSlotsKeys() {
  136. return Object.keys(this.$scopedSlots).filter((key) => !this.usedSlots.includes(key))
  137. },
  138. allSlotsKeys() {
  139. return this.slotsKeys.concat(this.scopedSlotsKeys)
  140. },
  141. // 切换全屏的按钮图标
  142. fullscreenButtonIcon() {
  143. return this.innerFullscreen ? 'fullscreen-exit' : 'fullscreen'
  144. },
  145. },
  146. watch: {
  147. visible() {
  148. if (this.visible) {
  149. this.innerFullscreen = this.fullscreen
  150. }
  151. },
  152. innerFullscreen(val) {
  153. this.$emit('update:fullscreen', val)
  154. },
  155. },
  156. methods: {
  157. getClass(clazz) {
  158. return { ...getClass(this), ...clazz }
  159. },
  160. getStyle(style) {
  161. return { ...getStyle(this), ...style }
  162. },
  163. close() {
  164. this.$emit('update:visible', false)
  165. },
  166. handleOk() {
  167. if (this.okClose) {
  168. this.close()
  169. }
  170. },
  171. handleCancel() {
  172. this.close()
  173. },
  174. /** 新手引导 */
  175. handleHelp() {
  176. let element = 'intro_cache_' + this.prefixNo
  177. Vue.ls.remove(element)
  178. handleIntroJs(this.prefixNo, 1)
  179. },
  180. /** 切换全屏 */
  181. toggleFullscreen() {
  182. this.innerFullscreen = !this.innerFullscreen
  183. triggerWindowResizeEvent()
  184. },
  185. },
  186. }
  187. </script>
  188. <style lang="less">
  189. .j-modal-box {
  190. &.fullscreen {
  191. top: 0;
  192. left: 0;
  193. padding: 0;
  194. height: 100vh;
  195. > .ant-modal-content {
  196. height: 100vh;
  197. border-radius: 0;
  198. > .ant-modal-body {
  199. /* title 和 footer 各占 55px */
  200. height: calc(100% - 55px - 55px);
  201. overflow: auto;
  202. }
  203. }
  204. > .no-title,
  205. &.no-footer {
  206. .ant-modal-body {
  207. height: calc(100% - 55px);
  208. }
  209. }
  210. > .no-title.no-footer {
  211. .ant-modal-body {
  212. height: 100%;
  213. }
  214. }
  215. }
  216. .j-modal-title-row {
  217. .left {
  218. width: calc(100% - 56px - 56px);
  219. }
  220. .right {
  221. width: 56px;
  222. position: inherit;
  223. .ant-modal-close {
  224. right: 56px;
  225. color: rgba(0, 0, 0, 0.45);
  226. &:hover {
  227. color: rgba(0, 0, 0, 0.75);
  228. }
  229. }
  230. }
  231. }
  232. }
  233. @media (max-width: 767px) {
  234. .j-modal-box.fullscreen {
  235. margin: 0;
  236. max-width: 100vw;
  237. }
  238. }
  239. </style>