小说后台管理系统
 
 
 

294 lines
7.2 KiB

  1. <template>
  2. <transition name="fade">
  3. <div
  4. v-if="isDialogExist"
  5. class="shade flex-box fullscreen"
  6. :style="styleData"
  7. >
  8. <div
  9. class="dialog-area"
  10. :class="{
  11. 'pl-54': isFullscreen && !opened,
  12. 'pl-210': isFullscreen && opened,
  13. 'ml-210': !isFullscreen && isLargeWidth && opened
  14. }"
  15. >
  16. <div class="title-area">
  17. <slot name="title">
  18. <span class="title">
  19. {{ title }}
  20. </span>
  21. <span v-if="subTitle.length > 0" class="sub-title">
  22. • {{ subTitle }}
  23. </span>
  24. </slot>
  25. <div class="icon-area ml-auto">
  26. <i class="el-icon-minus pointer" />
  27. <svg-icon
  28. v-if="!isFullscreen"
  29. icon-class="fullscreen"
  30. class="change-fullscreen-icon pointer"
  31. @click="fullscreen"
  32. />
  33. <svg-icon
  34. v-else
  35. icon-class="exit-fullscreen"
  36. class="change-fullscreen-icon pointer"
  37. @click="exitFullscreen"
  38. />
  39. <i class="el-icon-close pointer" @click="handleClose" />
  40. </div>
  41. </div>
  42. <div class="slot-place">
  43. <slot />
  44. </div>
  45. <div class="footer flex-box">
  46. <slot name="footer"></slot>
  47. </div>
  48. </div>
  49. </div>
  50. </transition>
  51. </template>
  52. <script>
  53. export default {
  54. name: 'Index',
  55. props: {
  56. isDialogExist: {
  57. type: Boolean,
  58. default: false
  59. },
  60. isDefaultFull: {
  61. type: Boolean,
  62. default: false
  63. },
  64. title: {
  65. type: String,
  66. default: '标题'
  67. },
  68. subTitle: {
  69. type: String,
  70. default: ''
  71. },
  72. dialogStyle: { type: Object, default: () => ({}), require: true }
  73. },
  74. data() {
  75. return {
  76. isFullscreen: false,
  77. styleData: {},
  78. styleTmp: {}
  79. }
  80. },
  81. computed: {
  82. opened() {
  83. return this.$store.state.app.sidebar.opened
  84. },
  85. isLargeWidth() {
  86. const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
  87. return parseInt(this.dialogStyle['--dialog-width']) >= vw - 420
  88. }
  89. },
  90. watch: {
  91. isDialogExist(n) {
  92. this.$nextTick(() => {
  93. if (n === true) { // 弹窗打开时
  94. // 检测到默认全屏时
  95. if (this.isDefaultFull) {
  96. this.fullscreen()
  97. }
  98. this.handleDialogDimension()
  99. window.addEventListener('resize', this.handleDialogDimension)
  100. } else {
  101. window.removeEventListener('resize', this.handleDialogDimension)
  102. // 关闭窗口时重置回原来的值
  103. this.$set(this.styleData, '--dialog-height', this.dialogStyle['--dialog-height'])
  104. this.$set(this.styleData, '--dialog-width', this.dialogStyle['--dialog-width'])
  105. }
  106. })
  107. }
  108. },
  109. created() {
  110. this.styleData = JSON.parse(JSON.stringify(this.dialogStyle))
  111. if (this.isDefaultFull) {
  112. this.fullscreen()
  113. }
  114. },
  115. methods: {
  116. handleDialogDimension() {
  117. /* if (this.styleData['--dialog-height'] === 'auto') {
  118. // 如果是auto,转为实际的高度
  119. this.styleData['--dialog-height'] = this.$el.offsetHeight
  120. }*/
  121. if (this.isFullscreen) {
  122. return false
  123. }
  124. // 视窗高度 document.documentElement.clientHeight(不含滚动条),window.innerHeight(含滚动条)
  125. const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
  126. // 宽度也一样处理
  127. const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
  128. // 弹窗高度大于或等于视窗高度
  129. if (parseInt(this.dialogStyle['--dialog-height']) >= vh) {
  130. this.$set(this.styleData, '--dialog-height', 0.9 * vh + 'px')
  131. } else {
  132. // 没有超过,恢复原值
  133. this.$set(this.styleData, '--dialog-height', this.dialogStyle['--dialog-height'])
  134. }
  135. if (parseInt(this.dialogStyle['--dialog-width']) >= vw - 420) {
  136. this.$set(this.styleData, '--dialog-width', 0.9 * vw + 'px')
  137. } else {
  138. // 没有超过,恢复原值
  139. this.$set(this.styleData, '--dialog-width', this.dialogStyle['--dialog-width'])
  140. }
  141. },
  142. fullscreen() {
  143. this.styleTmp = this.dialogStyle
  144. this.styleData = {
  145. '--dialog-width': '100vw',
  146. '--dialog-height': '100vh',
  147. '--dialog-border-radius': 0,
  148. '--title-height': this.dialogStyle['--title-height'],
  149. '--footer-height': this.dialogStyle['--footer-height'],
  150. '--offset': '64px'
  151. }
  152. this.isFullscreen = true
  153. this.$emit('fullscreen')
  154. },
  155. exitFullscreen() {
  156. this.styleData = JSON.parse(JSON.stringify(this.styleTmp))
  157. this.styleData['--offset'] = '0px'
  158. this.isFullscreen = false
  159. this.$emit('exitFullscreen')
  160. this.handleDialogDimension()
  161. },
  162. handleClose() {
  163. // 重置回原尺寸
  164. this.styleData = JSON.parse(JSON.stringify(this.dialogStyle))
  165. // 退出全屏
  166. this.isFullscreen = false
  167. this.$emit('close-dialog')
  168. }
  169. }
  170. }
  171. </script>
  172. <style lang="scss" scoped>
  173. .shade {
  174. --dialog-width: 0;
  175. --dialog-height: 0;
  176. --dialog-border-radius: 15px;
  177. --title-height: 0;
  178. --footer-height: 0px;
  179. --offset: 0px;
  180. position: fixed;
  181. top: 0;
  182. left: 0;
  183. background-color: rgba(0, 0, 0, 0.2);
  184. z-index: 999;
  185. .pl-54 {
  186. padding-left: 54px;
  187. }
  188. .pl-210 {
  189. padding-left: 210px;
  190. margin-top: 128px;
  191. .slot-place {
  192. height: calc(
  193. var(--dialog-height) - var(--title-height) - var(--footer-height) -
  194. var(--offset)
  195. ) !important;
  196. }
  197. }
  198. .ml-210 {
  199. margin-left: 210px !important;
  200. }
  201. .dialog-area {
  202. overflow: hidden;
  203. width: var(--dialog-width);
  204. height: var(--dialog-height);
  205. border-radius: var(--dialog-border-radius);
  206. background-color: #fff;
  207. box-shadow: 1px 1px 50px 0 rgba(0, 0, 0, 0.3);
  208. transition: all 0.3s;
  209. animation: enter 0.3s;
  210. @keyframes enter {
  211. 0% {
  212. opacity: 0;
  213. transform: translateY(-27px);
  214. }
  215. 100% {
  216. opacity: 1;
  217. transform: translateY(0);
  218. }
  219. }
  220. .title-area {
  221. display: flex;
  222. align-items: center;
  223. height: var(--title-height);
  224. background-color: #F9F9F9;
  225. .title {
  226. margin-left: 24px;
  227. font: 400 15px PingFangSC-Regular, PingFang SC;
  228. color: #333;
  229. }
  230. .sub-title {
  231. margin-left: 5px;
  232. font: 400 15px PingFangSC-Regular, PingFang SC;
  233. color: #FF4242;
  234. }
  235. .icon-area {
  236. width: 102px;
  237. .change-fullscreen-icon {
  238. margin: 0 20px 0 17px;
  239. color: #333;
  240. }
  241. }
  242. }
  243. .slot-place {
  244. overflow: auto;
  245. position: relative;
  246. width: 100%;
  247. height: calc(
  248. var(--dialog-height) - var(--title-height) - var(--footer-height)
  249. );
  250. border: 0 solid transparent;
  251. }
  252. }
  253. }
  254. .fade-enter-active,
  255. .fade-leave-active {
  256. transition: opacity 0.2s;
  257. }
  258. .fade-enter,
  259. .fade-leave-to {
  260. opacity: 0;
  261. }
  262. .footer {
  263. box-shadow: 0px -2px 4px 0px rgba(0, 0, 0, 0.04);
  264. padding: 12px;
  265. justify-content: center;
  266. align-content: center;
  267. text-align: center;
  268. height: var(--footer-height);
  269. }
  270. </style>