|
@@ -0,0 +1,427 @@
|
|
|
+<script lang="ts" setup>
|
|
|
+import {useZIndex, ElIcon, ElInput} from "element-plus";
|
|
|
+import {nextTick, onMounted, ref, h} from "vue";
|
|
|
+import sleep from "@/utils/sleep";
|
|
|
+// @ts-ignore
|
|
|
+import {
|
|
|
+ Close,
|
|
|
+ Warning,
|
|
|
+ SuccessFilled,
|
|
|
+ InfoFilled,
|
|
|
+ CircleCloseFilled
|
|
|
+} from '@element-plus/icons-vue'
|
|
|
+import {getCyId} from "@/components/xiao-chan/cy-message-box/cy-message-box";
|
|
|
+import {useCompRef} from "@/utils/useCompRef";
|
|
|
+
|
|
|
+const props = withDefaults(defineProps<{
|
|
|
+ message?: string;
|
|
|
+ showCancel?: boolean,
|
|
|
+ confirmClick?: (value: string) => void;
|
|
|
+ cancel?: (val: string) => void;
|
|
|
+ type?: 'success' | 'error' | 'warning' | 'info',
|
|
|
+ confirmButtonText?: string,
|
|
|
+ cancelButtonText?: string,
|
|
|
+ showInput?: boolean,
|
|
|
+ inputMinLength?: number,
|
|
|
+ inputMaxLength?: number,
|
|
|
+ inputValidator?: (val: string) => boolean | string,
|
|
|
+ inputErrorMessage?: string,
|
|
|
+ setClose?: (val: any) => void,
|
|
|
+ inputRows?: number,
|
|
|
+ boxId?: string,
|
|
|
+ allowToBeEmpty?: boolean,
|
|
|
+ dangerouslyUseHTMLString?: boolean
|
|
|
+}>(), {
|
|
|
+ showCancel: true,
|
|
|
+ confirmButtonText: '确认',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ inputErrorMessage: '校验未通过',
|
|
|
+ allowToBeEmpty: false,
|
|
|
+ dangerouslyUseHTMLString: false
|
|
|
+})
|
|
|
+
|
|
|
+const visible = ref(false)
|
|
|
+const headerRef = ref<HTMLHeadElement>(null)
|
|
|
+const inputValue = ref('')
|
|
|
+const errorMsg = ref('')
|
|
|
+const inputRef = useCompRef(ElInput)
|
|
|
+
|
|
|
+const inputId = ref(getCyId())
|
|
|
+
|
|
|
+let closeMode = ''
|
|
|
+
|
|
|
+const mainStyle = ref({
|
|
|
+ zIndex: 0,
|
|
|
+ display: 'flex'
|
|
|
+})
|
|
|
+
|
|
|
+const contentStyle = ref({
|
|
|
+ zIndex: 0,
|
|
|
+})
|
|
|
+
|
|
|
+function nextZIndex() {
|
|
|
+ return useZIndex().nextZIndex()
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 关闭动画
|
|
|
+ */
|
|
|
+function onAfterLeave() {
|
|
|
+ mainStyle.value.display = 'none'
|
|
|
+ if (!visible.value) {
|
|
|
+ if (closeMode === 'close' || closeMode === 'cancel') {
|
|
|
+ props.cancel(closeMode)
|
|
|
+ } else {
|
|
|
+ props.confirmClick(inputValue.value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 开启动画
|
|
|
+ */
|
|
|
+function onAfterEnter() {
|
|
|
+ mainStyle.value.display = 'flex'
|
|
|
+}
|
|
|
+
|
|
|
+function handleConfirm() {
|
|
|
+ if (props.showInput) {
|
|
|
+ handlePrompt()
|
|
|
+ } else {
|
|
|
+ visible.value = false;
|
|
|
+ closeMode = ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handlePrompt() {
|
|
|
+ function next() {
|
|
|
+ visible.value = false;
|
|
|
+ closeMode = ''
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!props.allowToBeEmpty) {
|
|
|
+ if (inputValue.value === '') {
|
|
|
+ errorMsg.value = '不允许为空。'
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (props.inputValidator) {
|
|
|
+ const inputValidator = props.inputValidator(inputValue.value)
|
|
|
+ if (typeof inputValidator === 'boolean') {
|
|
|
+ if (inputValidator) {
|
|
|
+ next()
|
|
|
+ } else {
|
|
|
+ errorMsg.value = props.inputErrorMessage
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (typeof inputValidator === 'string') {
|
|
|
+ if (inputValidator) {
|
|
|
+ errorMsg.value = inputValidator
|
|
|
+ } else {
|
|
|
+ next()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ next()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleClose(val) {
|
|
|
+ visible.value = false
|
|
|
+ closeMode = val
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function headerIcon() {
|
|
|
+ switch (props.type) {
|
|
|
+ case "success":
|
|
|
+ return h(SuccessFilled, null, null)
|
|
|
+ case "warning":
|
|
|
+ return h(Warning, null, null)
|
|
|
+ case 'info':
|
|
|
+ return h(InfoFilled, null, null)
|
|
|
+ case 'error':
|
|
|
+ return h(CircleCloseFilled)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function titleRender() {
|
|
|
+ const title = {
|
|
|
+ 'success': '成功',
|
|
|
+ 'warning': '警告',
|
|
|
+ 'info': '提示',
|
|
|
+ 'error': '错误',
|
|
|
+ }
|
|
|
+ return h('span', {style: {marginLeft: '8px'}}, title[props.type])
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ mainStyle.value.zIndex = nextZIndex()
|
|
|
+ contentStyle.value.zIndex = nextZIndex()
|
|
|
+ document.body.className = 'el-popup-parent--hidden'
|
|
|
+ await nextTick()
|
|
|
+ await sleep(100)
|
|
|
+ visible.value = true
|
|
|
+ props.setClose(close)
|
|
|
+ if (props.showInput) {
|
|
|
+ await inputRef.value.focus();
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+function close() {
|
|
|
+ handleClose('close')
|
|
|
+}
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="cy-message-box_main" :style="mainStyle" role="dialog" aria-modal="true">
|
|
|
+ <transition name="cy-message_show" @after-enter="onAfterEnter" @after-leave="onAfterLeave">
|
|
|
+ <div class="cy-message-box_body" v-show="visible" :style="contentStyle">
|
|
|
+ <div class="cy-message-box_close" @click="handleClose('close')">
|
|
|
+ <ElIcon @click="handleClose('close')">
|
|
|
+ <Close/>
|
|
|
+ </ElIcon>
|
|
|
+ </div>
|
|
|
+ <header ref="headerRef">
|
|
|
+ <el-icon :class="['cy-' + props.type]" class="cy-message_header-icon">
|
|
|
+ <component :is="headerIcon"/>
|
|
|
+ </el-icon>
|
|
|
+ <Component :is="titleRender"/>
|
|
|
+ </header>
|
|
|
+ <div class="cy_message-box_content">
|
|
|
+ <div class="message">
|
|
|
+ <component
|
|
|
+ v-if="props.dangerouslyUseHTMLString"
|
|
|
+ :for="inputId"
|
|
|
+ :is="props.showInput ? 'label' : 'span'"
|
|
|
+ v-html="props.message">
|
|
|
+ </component>
|
|
|
+ <component v-else
|
|
|
+ :is="props.showInput ? 'label' : 'span'"
|
|
|
+ :for="inputId">
|
|
|
+ {{ props.message }}
|
|
|
+ </component>
|
|
|
+ </div>
|
|
|
+ <div v-if="props.showInput" style="margin-top: 9px">
|
|
|
+ <ElInput
|
|
|
+ ref="inputRef"
|
|
|
+ :autofocus="true"
|
|
|
+ :rows="props.inputRows"
|
|
|
+ :type="props.inputRows === 0 ? '' : 'textarea'"
|
|
|
+ :minlength="props.inputMinLength"
|
|
|
+ :maxlength="props.inputMaxLength"
|
|
|
+ show-word-limit
|
|
|
+ :id="inputId"
|
|
|
+ v-model.trim="inputValue"/>
|
|
|
+ </div>
|
|
|
+ <div v-if="props.showInput" class="error_msg">
|
|
|
+ {{ errorMsg }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <footer>
|
|
|
+ <div class="cancel"
|
|
|
+ v-if="props.showCancel"
|
|
|
+ @click="handleClose('cancel')"
|
|
|
+ :style="{'--cy--bg-color': '245,108,108','--cy-color': '#F56C6C'}">
|
|
|
+ {{ props.cancelButtonText }}
|
|
|
+ </div>
|
|
|
+ <div class="confirm" :style="{'--cy--bg-color': '26, 92, 255','--cy-color': '#2C69FF'}"
|
|
|
+ @click="handleConfirm">
|
|
|
+ {{ props.confirmButtonText }}
|
|
|
+ </div>
|
|
|
+ </footer>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
+ </div>
|
|
|
+
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.cy-message_show-enter-active {
|
|
|
+ transform: scale(0.9);
|
|
|
+}
|
|
|
+
|
|
|
+.cy-message_show-enter-to {
|
|
|
+ transform: scale(1);
|
|
|
+}
|
|
|
+
|
|
|
+.cy-message_show-leave-active {
|
|
|
+ transform: scale(1);
|
|
|
+}
|
|
|
+
|
|
|
+.cy-message_show-leave-to {
|
|
|
+ transform: scale(0);
|
|
|
+}
|
|
|
+
|
|
|
+.cy-message-box_main {
|
|
|
+ background: rgba(0, 0, 0, .2);
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ justify-content: center;
|
|
|
+ align-content: center;
|
|
|
+ -webkit-backdrop-filter: saturate(180%) blur(15px);
|
|
|
+ backdrop-filter: saturate(180%) blur(15px);
|
|
|
+
|
|
|
+ .cy-message-box_body {
|
|
|
+ height: max-content;
|
|
|
+ width: 418px;
|
|
|
+ margin: auto;
|
|
|
+ background-color: white;
|
|
|
+ border-radius: 20px;
|
|
|
+ padding: 5px;
|
|
|
+ position: relative;
|
|
|
+ transition: transform .2s ease-out;
|
|
|
+
|
|
|
+ .cy-message-box_close {
|
|
|
+ position: absolute;
|
|
|
+ top: -6px;
|
|
|
+ right: -6px;
|
|
|
+ border: 0;
|
|
|
+ height: 34px;
|
|
|
+ width: 34px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: inherit;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 5px 20px 0 rgba(0, 0, 0, .05);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 20px;
|
|
|
+ color: black;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ -webkit-box-shadow: 0 0 4px 0 rgba(0, 0, 0, .05);
|
|
|
+ box-shadow: 0 0 4px 0 rgba(0, 0, 0, .05);
|
|
|
+ transform: translate(-2px, 2px);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ header {
|
|
|
+ user-select: none;
|
|
|
+ display: -webkit-box;
|
|
|
+ display: -ms-flexbox;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: start;
|
|
|
+ padding: 10px 16px;
|
|
|
+ box-sizing: border-box !important;
|
|
|
+
|
|
|
+ span {
|
|
|
+ margin-left: 8px;
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cy-message_header-icon {
|
|
|
+ font-size: 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cy-warning {
|
|
|
+ color: #e6a23c;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cy-success {
|
|
|
+ color: #67c23a;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cy-info {
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cy-error {
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .cy_message-box_content {
|
|
|
+ padding: 10px 16px;
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+ border-radius: inherit;
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ .error_msg {
|
|
|
+ color: #f56c6c;
|
|
|
+ font-size: 12px;
|
|
|
+ min-height: 18px;
|
|
|
+ margin-top: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .message {
|
|
|
+ box-sizing: border-box;
|
|
|
+ min-height: 20px;
|
|
|
+ max-height: 200px;
|
|
|
+ overflow: auto;
|
|
|
+
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 5px;
|
|
|
+ height: 5px;
|
|
|
+ display: block;
|
|
|
+ background: white;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-thumb {
|
|
|
+ background: #2C3E50;
|
|
|
+ border-radius: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ footer {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-end;
|
|
|
+
|
|
|
+ div {
|
|
|
+ width: 85px;
|
|
|
+ line-height: 39px;
|
|
|
+ text-align: center;
|
|
|
+ border-radius: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ color: var(--cy-color);
|
|
|
+ outline: none;
|
|
|
+ list-style: none;
|
|
|
+ user-select: none;
|
|
|
+
|
|
|
+ &:hover:before {
|
|
|
+ opacity: 1;
|
|
|
+ transform: scale(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:before {
|
|
|
+ content: "";
|
|
|
+ background: rgba(var(--cy--bg-color), .1);
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ border-radius: inherit;
|
|
|
+ pointer-events: none;
|
|
|
+ -webkit-transition: all .25s ease;
|
|
|
+ transition: all .25s ease;
|
|
|
+ z-index: -1;
|
|
|
+ transform: scale(.5);
|
|
|
+ opacity: 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|