wd-message-box.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <template>
  2. <view>
  3. <wd-popup
  4. transition="zoom-in"
  5. v-model="messageState.show"
  6. :close-on-click-modal="messageState.closeOnClickModal"
  7. :lazy-render="messageState.lazyRender"
  8. custom-class="wd-message-box"
  9. @click-modal="toggleModal('modal')"
  10. :z-index="messageState.zIndex"
  11. :duration="200"
  12. :root-portal="rootPortal"
  13. >
  14. <view :class="rootClass">
  15. <view :class="bodyClass">
  16. <view v-if="messageState.title" class="wd-message-box__title">
  17. {{ messageState.title }}
  18. </view>
  19. <view class="wd-message-box__content">
  20. <block v-if="messageState.type === 'prompt'">
  21. <wd-input
  22. v-model="messageState.inputValue"
  23. :type="messageState.inputType"
  24. :size="messageState.inputSize"
  25. :placeholder="messageState.inputPlaceholder"
  26. @input="inputValChange"
  27. />
  28. <view v-if="messageState.showErr" class="wd-message-box__input-error">
  29. {{ messageState.inputError || translate('inputNoValidate') }}
  30. </view>
  31. </block>
  32. <slot>{{ messageState.msg }}</slot>
  33. </view>
  34. </view>
  35. <view :class="`wd-message-box__actions ${messageState.showCancelButton ? 'wd-message-box__flex' : 'wd-message-box__block'}`">
  36. <wd-button v-bind="customCancelProps" v-if="messageState.showCancelButton" @click="toggleModal('cancel')">
  37. {{ messageState.cancelButtonText || translate('cancel') }}
  38. </wd-button>
  39. <wd-button v-bind="customConfirmProps" @click="toggleModal('confirm')">
  40. {{ messageState.confirmButtonText || translate('confirm') }}
  41. </wd-button>
  42. </view>
  43. </view>
  44. </wd-popup>
  45. </view>
  46. </template>
  47. <script lang="ts">
  48. export default {
  49. name: 'wd-message-box',
  50. options: {
  51. virtualHost: true,
  52. addGlobalClass: true,
  53. styleIsolation: 'shared'
  54. }
  55. }
  56. </script>
  57. <script lang="ts" setup>
  58. import wdPopup from '../wd-popup/wd-popup.vue'
  59. import wdButton from '../wd-button/wd-button.vue'
  60. import wdInput from '../wd-input/wd-input.vue'
  61. import { computed, inject, reactive, ref, watch } from 'vue'
  62. import { messageBoxProps, type MessageOptionsWithCallBack, type MessageResult } from './types'
  63. import { defaultOptions, getMessageDefaultOptionKey } from '.'
  64. import { deepAssign, isDef, isFunction, isUndefined, omitBy } from '../common/util'
  65. import { useTranslate } from '../composables/useTranslate'
  66. import type { ButtonProps } from '../wd-button/types'
  67. const props = defineProps(messageBoxProps)
  68. const { translate } = useTranslate('message-box')
  69. const rootClass = computed(() => {
  70. return `wd-message-box__container ${props.customClass}`
  71. })
  72. const bodyClass = computed(() => {
  73. return `wd-message-box__body ${!messageState.title ? 'is-no-title' : ''} ${messageState.type === 'prompt' ? 'is-prompt' : ''}`
  74. })
  75. const messageOptionKey = getMessageDefaultOptionKey(props.selector)
  76. const messageOption = inject(messageOptionKey, ref<MessageOptionsWithCallBack>(defaultOptions)) // message选项
  77. const messageState = reactive<MessageOptionsWithCallBack>({
  78. msg: '', // 消息内容
  79. show: false, // 是否显示弹框
  80. title: '', // 标题
  81. showCancelButton: false, // 是否展示取消按钮
  82. closeOnClickModal: true, // 是否支持点击蒙层关闭
  83. confirmButtonText: '', // 确定按钮文案
  84. cancelButtonText: '', // 取消按钮文案
  85. type: 'alert', // 弹框类型
  86. inputType: 'text', // 输入框类型
  87. inputValue: '', // 输入框初始值
  88. inputPlaceholder: '', // 输入框placeholder
  89. inputError: '', // 输入框错误提示文案
  90. showErr: false, // 是否显示错误提示
  91. zIndex: 99, // 弹窗层级
  92. lazyRender: true // 弹层内容懒渲染
  93. })
  94. /**
  95. * 确认按钮属性
  96. */
  97. const customConfirmProps = computed(() => {
  98. const buttonProps: Partial<ButtonProps> = deepAssign(
  99. {
  100. block: true
  101. },
  102. isDef(messageState.confirmButtonProps) ? omitBy(messageState.confirmButtonProps, isUndefined) : {}
  103. )
  104. buttonProps.customClass = `${buttonProps.customClass || ''} wd-message-box__actions-btn`
  105. return buttonProps
  106. })
  107. /**
  108. * 取消按钮属性
  109. */
  110. const customCancelProps = computed(() => {
  111. const buttonProps: Partial<ButtonProps> = deepAssign(
  112. {
  113. block: true,
  114. type: 'info'
  115. },
  116. isDef(messageState.cancelButtonProps) ? omitBy(messageState.cancelButtonProps, isUndefined) : {}
  117. )
  118. buttonProps.customClass = `${buttonProps.customClass || ''} wd-message-box__actions-btn`
  119. return buttonProps
  120. })
  121. // 监听options变化展示
  122. watch(
  123. () => messageOption.value,
  124. (newVal: MessageOptionsWithCallBack) => {
  125. reset(newVal)
  126. },
  127. {
  128. deep: true,
  129. immediate: true
  130. }
  131. )
  132. watch(
  133. () => messageState.show,
  134. (newValue) => {
  135. resetErr(!!newValue)
  136. },
  137. {
  138. deep: true,
  139. immediate: true
  140. }
  141. )
  142. /**
  143. * 点击操作
  144. * @param action
  145. */
  146. function toggleModal(action: 'confirm' | 'cancel' | 'modal') {
  147. if (action === 'modal' && !messageState.closeOnClickModal) {
  148. return
  149. }
  150. if (messageState.type === 'prompt' && action === 'confirm' && !validate()) {
  151. return
  152. }
  153. switch (action) {
  154. case 'confirm':
  155. if (messageState.beforeConfirm) {
  156. messageState.beforeConfirm({
  157. resolve: (isPass) => {
  158. if (isPass) {
  159. handleConfirm({
  160. action: action,
  161. value: messageState.inputValue
  162. })
  163. }
  164. }
  165. })
  166. } else {
  167. handleConfirm({
  168. action: action,
  169. value: messageState.inputValue
  170. })
  171. }
  172. break
  173. case 'cancel':
  174. handleCancel({
  175. action: action
  176. })
  177. break
  178. default:
  179. handleCancel({
  180. action: 'modal'
  181. })
  182. break
  183. }
  184. }
  185. /**
  186. * 确认回调
  187. * @param result
  188. */
  189. function handleConfirm(result: MessageResult) {
  190. messageState.show = false
  191. if (isFunction(messageState.success)) {
  192. messageState.success(result)
  193. }
  194. }
  195. /**
  196. * 取消回调
  197. * @param result
  198. */
  199. function handleCancel(result: MessageResult) {
  200. messageState.show = false
  201. if (isFunction(messageState.fail)) {
  202. messageState.fail(result)
  203. }
  204. }
  205. /**
  206. * 如果存在校验规则行为,则进行判断校验是否通过规则。默认不存在校验直接铜鼓。
  207. */
  208. function validate() {
  209. if (messageState.inputPattern && !messageState.inputPattern.test(String(messageState.inputValue))) {
  210. messageState.showErr = true
  211. return false
  212. }
  213. if (typeof messageState.inputValidate === 'function') {
  214. const validateResult = messageState.inputValidate(messageState.inputValue!)
  215. if (!validateResult) {
  216. messageState.showErr = true
  217. return false
  218. }
  219. }
  220. messageState.showErr = false
  221. return true
  222. }
  223. /**
  224. * @description show关闭时,销毁错误提示
  225. * @param val
  226. */
  227. function resetErr(val: boolean) {
  228. if (val === false) {
  229. messageState.showErr = false
  230. }
  231. }
  232. function inputValChange({ value }: { value: string | number }) {
  233. if (value === '') {
  234. messageState.showErr = false
  235. return
  236. }
  237. messageState.inputValue = value
  238. }
  239. /**
  240. * 重置message选项值
  241. * @param option message选项值
  242. */
  243. function reset(option: MessageOptionsWithCallBack) {
  244. if (option) {
  245. messageState.title = isDef(option.title) ? option.title : ''
  246. messageState.showCancelButton = isDef(option.showCancelButton) ? option.showCancelButton : false
  247. messageState.show = option.show
  248. messageState.closeOnClickModal = option.closeOnClickModal
  249. messageState.confirmButtonText = option.confirmButtonText
  250. messageState.cancelButtonText = option.cancelButtonText
  251. messageState.msg = option.msg
  252. messageState.type = option.type
  253. messageState.inputType = option.inputType
  254. messageState.inputSize = option.inputSize
  255. messageState.inputValue = option.inputValue
  256. messageState.inputPlaceholder = option.inputPlaceholder
  257. messageState.inputPattern = option.inputPattern!
  258. messageState.inputValidate = option.inputValidate
  259. messageState.success = option.success
  260. messageState.fail = option.fail
  261. messageState.beforeConfirm = option.beforeConfirm
  262. messageState.inputError = option.inputError
  263. messageState.showErr = option.showErr
  264. messageState.zIndex = option.zIndex
  265. messageState.lazyRender = option.lazyRender
  266. messageState.confirmButtonProps = option.confirmButtonProps
  267. messageState.cancelButtonProps = option.cancelButtonProps
  268. }
  269. }
  270. </script>
  271. <style lang="scss" scoped>
  272. @import './index.scss';
  273. </style>