Browse Source

定位提示弹出框改造-可以动态修改文案,手动控制显示隐藏

zq-0828-newMenu
zq 22 hours ago
parent
commit
9fd57d14f3
  1. 251
      src/components/Tooltip.vue
  2. 49
      src/utils/tooltip.js
  3. 51
      src/views/elementGroups.vue

251
src/components/Tooltip.vue

@ -1,9 +1,8 @@
<template>
<template>
<transition name="fade">
<div v-if="visible" :class="['position-message', type, position]" id="qq" :style="positionStyle">
<!-- <i :class="iconClass"></i>/ -->
<div v-if="visible" :class="['position-message', type, position]" :style="positionStyle">
<img :src="defaultImages[type]" class="message-image" />
<span>{{ message }}</span>
<span>{{ currentMessage }}</span>
</div>
</transition>
</template>
@ -14,7 +13,7 @@ export default {
type: {
type: String,
default: 'info',
validator: val => ['info', 'success', 'error'].includes(val)
validator: val => ['info', 'success', 'error', 'warning'].includes(val)
},
position: {
type: String,
@ -22,22 +21,37 @@ export default {
validator: val => ['top', 'bottom'].includes(val)
},
message: String,
target: null, // /DOM
target: null,
offset: {
type: Number,
default: 10
},
duration: {
type: Number,
default: 2000
}
},
data() {
return {
visible: false,
positionStyle: {},
defaultImages:{
currentMessage: this.message, // 使message
defaultImages: {
success: require('@/assets/message_Success.png'),
info: require('@/assets/message_Warning.png'),
warning: require('@/assets/message_Warning.png'),
error: require('@/assets/message_error.png')
}
},
timer: null
}
},
watch: {
// propsmessage
message(newVal) {
this.currentMessage = newVal;
this.$nextTick(() => {
this.calculatePosition();
});
}
},
computed: {
@ -53,15 +67,67 @@ export default {
show() {
this.visible = true
this.$nextTick(() => {
this.calculatePosition(); // DOM
this.calculatePosition();
// duration0
if (this.duration > 0) {
this.clearTimer();
this.timer = setTimeout(() => {
this.hide();
}, this.duration);
}
});
console.log('zoulma???????', this.visible);
return this //
return this;
},
hide() {
this.visible = false
this.visible = false;
this.clearTimer();
return this;
},
updateMessage(newMessage) {
this.currentMessage = newMessage;
this.$nextTick(() => {
this.calculatePosition();
});
return this;
},
updateConfig(newConfig) {
//
if (newConfig.message !== undefined) {
this.currentMessage = newConfig.message;
}
if (newConfig.type) {
this.$emit('update:type', newConfig.type);
}
if (newConfig.position) {
this.$emit('update:position', newConfig.position);
}
this.$nextTick(() => {
this.calculatePosition();
});
return this;
},
destroy() {
this.hide();
// DOM
setTimeout(() => {
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
}
this.$destroy();
}, 300);
},
clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
},
calculatePosition() {
if (!this.$el || !this.visible) return;
const targetEl = typeof this.target === 'string'
? document.querySelector(this.target)
: this.target?.$el || this.target;
@ -75,28 +141,43 @@ export default {
const messageWidth = this.$el.offsetWidth;
const viewportWidth = window.innerWidth;
const messageHeight = this.$el.offsetHeight;
const scrollY = window.scrollY || window.pageYOffset;
//
let left = targetRect.left + targetRect.width / 2 - messageWidth / 2;
let top = 0;
//
if (left < 0) {
left = targetRect.left; //
} else if (left + messageWidth > viewportWidth) {
left = viewportWidth - messageWidth - (viewportWidth -targetRect.right); //
if (left < 5) {
left = 5; //
} else if (left + messageWidth > viewportWidth - 5) {
left = viewportWidth - messageWidth - 5; //
}
if(this.position == 'top'){
top =`${targetRect.top + window.scrollY - this.offset - messageHeight}px`
}else{
top = `${targetRect.top + window.scrollY + this.offset + messageHeight}px`
if (this.position === 'top') {
top = targetRect.top + scrollY - this.offset - messageHeight;
//
if (top < scrollY) {
top = targetRect.bottom + scrollY + this.offset;
}
} else {
top = targetRect.bottom + scrollY + this.offset;
//
if (top + messageHeight > scrollY + window.innerHeight) {
top = targetRect.top + scrollY - this.offset - messageHeight;
}
}
this.positionStyle = {
top,
top: `${top}px`,
left: `${left}px`,
right: 'auto', // right
right: 'auto'
};
},
},
beforeDestroy() {
this.clearTimer();
}
}
</script>
@ -112,133 +193,29 @@ export default {
background: #FFFFFF;
box-sizing: border-box;
box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.16);
span{
pointer-events: none;
span {
letter-spacing: 0.08em;
color: #626573;
white-space: nowrap;
}
}
.message-image {
width: 18px;
height: 18px;
margin-right: 10px;
}
.position-message i {
margin-right: 6px;
font-size: 16px;
}
.position-message.info {
/* background-color: #f4f4f5;
color: #909399;
border: 1px solid #e9e9eb; */
}
.position-message.success {
/* background-color: #f0f9eb;
color: #67c23a;
border: 1px solid #e1f3d8; */
}
.position-message.error {
/* background-color: #fef0f0;
color: #f56c6c;
border: 1px solid #fde2e2; */
width: 18px;
height: 18px;
margin-right: 10px;
flex-shrink: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
transition: opacity 0.3s, transform 0.3s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
</style>
<!-- <template>
<transition name="fade">
<div
v-if="visible"
:class="['position-message', type]"
:style="positionStyle"
>
<i :class="iconClass"></i>
<span>{{ message }}</span>
</div>
</transition>
</template>
<script>
export default {
computed: {
iconClass() {
return {
info: 'el-icon-info',
success: 'el-icon-success',
error: 'el-icon-error'
}[this.type]
}
},
props: {
type: {
type: String,
default: 'info',
validator: val => ['info', 'success', 'error'].includes(val)
},
position: {
type: String,
default: 'top',
validator: val => ['top', 'bottom'].includes(val)
},
message: String,
target: null, // /DOM
offset: {
type: Number,
default: 5
}
},
data() {
return {
visible: false,
positionStyle: {}
}
},
methods: {
hide() {
this.visible = false
},
calculatePosition() {
const targetEl = typeof this.target === 'string'
? document.querySelector(this.target)
: this.target?.$el || this.target;
if (!targetEl?.getBoundingClientRect) {
console.error('Invalid target:', this.target);
return;
}
const targetRect = targetEl.getBoundingClientRect();
const messageWidth = this.$el.offsetWidth;
if (this.position === 'bottom') {
this.positionStyle = {
top: `${targetRect.bottom + window.scrollY + this.offset}px`,
left: `${targetRect.left + targetRect.width / 2 - messageWidth / 2}px`,
};
} else {
//
this.positionStyle = {
top: `${targetRect.top + window.scrollY - this.offset - this.$el.offsetHeight}px`,
left: `${targetRect.left + targetRect.width / 2 - messageWidth / 2}px`,
};
}
},
show() {
this.visible = true;
this.$nextTick(this.calculatePosition);
return this;
},
},
};
</script> -->
</style>

49
src/utils/tooltip.js

@ -3,31 +3,56 @@ import Tooltip from '@/components/Tooltip.vue'
const PositionMessageConstructor = Vue.extend(Tooltip)
// 自动隐藏功能扩展
// 存储实例引用,便于全局控制
const instances = []
function showMessage(options) {
const instance = new PositionMessageConstructor({
propsData: options
propsData: options
}).$mount()
document.body.appendChild(instance.$el)
instances.push(instance)
const showInstance = instance.show()
// 添加自动隐藏
// 添加自动隐藏(duration为0表示一直显示)
if (options.duration !== 0) {
setTimeout(() => {
showInstance.hide()
const duration = options.duration || 2000
setTimeout(() => {
document.body.removeChild(instance.$el)
}, 300) // 等待动画结束
}, options.duration || 2000)
instance.hide()
setTimeout(() => {
if (instance.$el && instance.$el.parentNode) {
instance.$el.parentNode.removeChild(instance.$el)
}
// 从实例数组中移除
const index = instances.indexOf(instance)
if (index > -1) {
instances.splice(index, 1)
}
}, 300) // 等待动画结束
}, duration)
}
return showInstance
}
}
// 隐藏所有tooltip
function hideAll() {
instances.forEach(instance => {
instance.hide()
setTimeout(() => {
if (instance.$el && instance.$el.parentNode) {
instance.$el.parentNode.removeChild(instance.$el)
}
}, 300)
})
instances.length = 0
}
export default {
install(Vue) {
Vue.prototype.$positionMessage = showMessage
}
install(Vue) {
Vue.prototype.$positionMessage = showMessage
Vue.prototype.$hideAllPositionMessages = hideAll
}
}

51
src/views/elementGroups.vue

@ -74,11 +74,17 @@
@change="radioChange" :rules="rules.language" label-key="name" :disabledKeys="['1']"
value-key="id" />
</div>
<div class="ele-item">
<label for="">数组套对象类型下拉框</label>
<!-- :extraItem="{label:'全部',value:'99999'}" 传入必须是 labelvalue 组件内会自动改为自定义的 labelKeyvalueKey-->
<div class="flex">
<GuipSelect width="150px" v-model="form.card" clearable label="卡片" :default-value="form.card" @change="selectChangeTest"
prop="card" :options="options" valueKey="id1" labelKey="id2" :extraItem="{label:'全部',value:'99999'}"/>
<p>j卢卡斯就到啦</p>
<GuipInput ref="GuipInput" v-model="form.input2" :maxlength="100" @change="handleInput"
@blur="handleInput" prop="input2" @input="handleInput" @focus="handleInput" placeholder="这是自定义默认提示语" />
</div>
</div>
<div class="ele-item">
<label for="">纯数组[1,2]下拉框</label>
@ -90,6 +96,8 @@
<GuipSelect width="150px" v-model="form.card1" clearable label="卡片" :default-value="form.card" @change="selectChangeTest"
prop="card" :options="languageOptions"/>
</div>
<el-button type="primary" @click="submitForm">Submit</el-button>
<div>
<h3 for="">表格(表头自定义自定义渲染固定列)</h3>
<GuipButton type="primary" @click="toggleAllSelection">全选按钮</GuipButton>
@ -237,6 +245,7 @@
</GuipInput>
<!-- <el-input placeholder="oieuwroieuwi" style="width:400px;height:60px"></el-input> -->
</div>
<div class="ele-item">
<label for="">单选框</label>
<el-radio v-model="radio1" :label="1">选项一</el-radio>
@ -494,7 +503,6 @@
</CustomDropdown>
</div>
<el-button type="primary" @click="submitForm">Submit</el-button>
</el-form>
<div style="width: 100%;margin: 20px 0;height: 20px;background-color: antiquewhite;">
@ -523,12 +531,18 @@
<el-button @click="save" type="primary">保存</el-button>
</div> -->
</div>
<div class="flex ele-item">
<div class="flex ele-item gap10" style="flex-wrap: wrap;">
<label for="">一些功能集合</label>
<GuipButton type="primary" ref="button1" :btnstyle="{ width: '300px' }" @click="openDialog3('button1','bottom')">下方弹出消息提示
</GuipButton>
<GuipButton type="primary" ref="button2" :btnstyle="{ width: '300px' }" @click="openDialog3('button2','top')">上方弹出消息提示
</GuipButton>
<GuipButton type="primary" ref="button3" :btnstyle="{ width: '300px' }" @click="openDialog4('button3','top')">一直显示消息提示
</GuipButton>
<GuipButton type="primary" ref="button5" :btnstyle="{ width: '300px' }" @click="openDialog5">动态更改提示文案
</GuipButton>
<GuipButton type="primary" :btnstyle="{ width: '300px' }" @click="openDialog6">主动隐藏消息提示
</GuipButton>
<GuipButton type="primary" :btnstyle="{ width: '300px' }" @click="openDialog">打开弹框标题巨左按钮居右
</GuipButton>
<GuipButton type="primary" :btnstyle="{ width: '300px' }" @click="openDialog1">打开弹框标题巨中按钮居中
@ -744,9 +758,12 @@ export default {
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' }
],
input2: [
{ required: true, message: '请输入手机号', trigger: 'blur' }
],
age: [
{ required: true, message: '请输入年龄', trigger: 'blur' }
]
],
},
usernameRules: [{ required: true, message: 'Username is required', trigger: 'blur' }],//rules
msg: "测试",
@ -940,7 +957,6 @@ export default {
height: '50px'
},
plain: false,
options: [{
value: '选项1',
label1: '黄金hhhhhh',
@ -972,6 +988,8 @@ export default {
label1: '双皮奶hhhhhhhhh',
label: '北京烤鸭'
}],
tooltip:null
}
},
mounted() {
@ -1162,15 +1180,40 @@ export default {
openDialog2() {
this.dialogVisible2 = true;
// // tooltip
// this.tooltip.destroy()
// // tooltip
// this.$hideAllPositionMessages()
},
openDialog6(){
this.tooltip.hide()
},
openDialog3(el,pos){
// type
this.$positionMessage({
duration:3000,
type: 'success',
message: '操作成功',
target: this.$refs[el], // DOM
position: pos // 'bottom'
}) // 3
console.log(this.tooltip,'tooltip',el,pos);
},
openDialog4(el){
//
this.tooltip = this.$positionMessage({
type: 'info',
message: '这个提示会一直显示',
target: this.$refs[el],
duration: 0 // 0
})
},
openDialog5(){
this.tooltip.updateMessage('更新后的文本内容----')
},
//
handleConfirm() {

Loading…
Cancel
Save