<template>
<el-tooltip :content="value" :disabled="tooltipDisabled || !showTooltip" :effect="effect" :placement="placement">
<el-input
v-bind="$attrs"
:class="customClass"
v-model="value"
oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
ref="inputRef"
:disabled="disabled"
:value="isfocus ? value : formatMoney(value, 2)"
@mouseenter="judgeMouseEnter"
@mouseleave="judgeMouseLeave"
@change="changeContent($event)"
@focus="isfocus = true"
@blur="isfocus = false"
>
<template #prepend v-if="$slots.prepend">
<slot name="prepend"></slot>
</template>
<template #append v-if="$slots.append">
<slot name="append"></slot>
</template>
</el-input>
</el-tooltip>
</template>
<script lang="ts">
import { computed, defineComponent, reactive, toRefs, watch } from 'vue';
export default defineComponent({
name: 'FmisMoneyInput',
props: {
modelValue: {
type: [String, Number],
default: '',
},
effect: {
type: String,
default: 'dark',
},
placement: {
type: String,
default: 'top',
},
tooltipDisabled: {
type: Boolean,
default: !true,
},
disabled: {
type: Boolean,
default: false,
},
customClass: {
type: String,
default: '',
},
// 是否可以输入负数 默认可以 不可以传false
isComplex: {
type: Boolean,
default: true,
},
},
setup(props, context) {
let padding = 0;
const state = reactive({
input: <any>null,
fakeEle: <any>null,
showTooltip: false,
value: props.modelValue,
isfocus: false,
});
const refs = reactive({
inputRef: <any>null,
});
// 四舍五入
const round = (num: any, scale?: number) => {
if (num == null) {
return 0;
}
if (scale == null) {
scale = 0;
}
const pow = Math.pow(10, scale);
return Math.round((num * (pow * 10)) / 10) / pow;
};
// 格式化金额
const formatMoney = (val: any, precision: any, prefix?: string, suffix?: string) => {
if (val === null || val === '' || val === undefined) {
return val;
}
if (prefix == null) {
prefix = '';
}
if (suffix == null) {
suffix = '';
}
let value = '';
if (precision === null || precision === '') {
value = val + '';
} else {
value = parseFloat(val).toFixed(precision) + '';
}
const groupSeparator = ','; // 分隔符
const decimalSeparator = '.'; // 分隔小数和整数部门
let s1 = value;
let s2 = '';
const dpos = value.indexOf('.');
if (dpos >= 0) {
s1 = value.substring(0, dpos);
s2 = value.substring(dpos + 1, value.length);
}
if (groupSeparator) {
const p = /(\d+)(\d{3})/;
while (p.test(s1)) {
s1 = s1.replace(p, '$1' + groupSeparator + '$2');
}
}
if (s2) {
return prefix + s1 + decimalSeparator + s2 + suffix;
} else {
return prefix + s1 + suffix;
}
};
const judgeMouseEnter = () => {
// console.log('鼠标移入');
state.input = findInnerInput(Array.from(refs.inputRef.$el.children));
// 未匹配到内部input元素
if (!state.input) return;
padding = getInputPaddingCount();
const styles = window.getComputedStyle(state.input);
state.fakeEle = document.createElement('div');
// 将输入框元素的 font 属性赋给辅助的 div 元素
state.fakeEle.style.font = styles.font;
state.fakeEle.style.cssText = 'position:absolute;left:-9999px;visibility:hidden;white-space:nowrap;';
document.body.appendChild(state.fakeEle);
showTooltipChange(props.modelValue);
};
const judgeMouseLeave = () => {
// console.log('鼠标移出');
// 移除临时节点
if (state.fakeEle?.parentNode) {
document.body.removeChild(state.fakeEle);
}
};
const showTooltipChange = (val: any) => {
if (val === '' || !state.fakeEle || !state.input) {
state.showTooltip = false;
return;
}
state.fakeEle.innerHTML = val;
state.showTooltip = state.fakeEle.offsetWidth > state.input.offsetWidth - padding;
};
// 查找input文本元素
const findInnerInput = (nodes: any[]) => {
let findNode = null;
nodes.forEach((node) => {
if (node.className === 'el-input__inner') {
findNode = node;
} else if (node.className === 'el-input__wrapper') {
Object.values(node.children).forEach((childNode: any) => {
if (childNode.className === 'el-input__inner') {
findNode = childNode;
}
});
}
});
return findNode;
};
// 获取input内容元素padding边距
const getInputPaddingCount = () => {
const styles = window.getComputedStyle(state.input);
return parseInt(styles.paddingLeft) + parseInt(styles.paddingRight);
};
watch(
() => props.modelValue,
(value) => {
showTooltipChange(value);
}
);
// change事件解决有时候 输过值 但是显示无的问题
const changeContent = (val: any) => {
state.isfocus = false;
// 输入小数点后位数太多 默认保留两位小数(四舍五入)
state.value = round(val, 2);
// 1 判断能否输入负数
if (props.isComplex == false && val.startsWith('-')) {
// console.log('不能输入负数');
state.value = Number(val.slice(1));
}
context.emit('onChange', state.value);
context.emit('update:modelValue', state.value);
};
watch(
()=> props.modelValue,
(val)=>{
state.value = val;
}
)
return {
...toRefs(state),
...toRefs(refs),
judgeMouseEnter,
judgeMouseLeave,
changeContent,
formatMoney,
};
},
});
</script>
基于el-input包装的金额输入框,展示时千分位分割,输入时是数字。
于 2023-11-30 21:00:00 首次发布