(function (window, $) { // 模块加载横幅(排查用):仅在 ?otcdebug=1 或 localStorage.otcdebug=1 开启时打印,含 bundle 版本+git sha。 // 注意:本文件在 bundle.js 中早于 main.js(otcDebug 定义处)加载,故直接读 window.ylotc.__diag,不依赖 otcDebug。 var __diag = window.ylotc && window.ylotc.__diag; if (__diag && __diag.debug && window.console) { var git = (__diag.git || '').slice(0, 7); console.log('%c[fastVue.base.js] v1.4.2 (bundle=' + __diag.jsVersion + ', git=' + git + ', built=' + __diag.built + ')', 'color:#06c;font-weight:bold'); } function FastVue() { } FastVue.noop = function (a, b, c) { }; //解析数字,返回值:NAN或正常数字或''(输入参数为空) FastVue.parseNumber = function (val,percent) { switch (typeof val) { case 'number': return val; case 'string': { val = val.trim(); if (val === '') return ''; let isPercent = val.endsWith('%') || percent==true; if (isPercent) { val = val.replace(/%+$/, ''); if (val === '') return ''; } if (val.endsWith('‱')) { val = val.replace(/‱+$/, ''); if (val === '') return ''; } let number = new Number(val); return isPercent ? number / 100 : number; } default: return Number.NaN; } }; FastVue.extend = $.extend; FastVue.setWidth = function (el, width) { width && (el.style.width = typeof width === 'number' ? width + 'px' : width); }; FastVue.getDomElement = function (el) { if (!el) return null; if (typeof el === 'string') { return document.getElementById(el.startsWith('#') ? el.substring(1) : el); } el instanceof jQuery && (el = el[0]); return el; }; function Form(options) { if (!(this instanceof Form)) { throw 'FastVue.Form is a constructor and should be called with the `new` keyword'; } this.data = {}; this.layout = { display: 'inline', title: { width: 100 }, input: { width: '165px' } }; this.submit = { url: '', before() { }, after() { } }; this.fields = {}; this.view = {}; this.init(options); } FastVue.Form = Form; window.FastVue = FastVue; }(window, jQuery)); //autocomplete (function (window, $) { function __resetLookup(options, context) { if (options.lookupFn) { let lookupFn = options.lookupFn; return options.lookup = function (query, callback) { let queryLowerCase = (query || '').toLowerCase(); let suggestions = lookupFn.call(context) || []; if (queryLowerCase) { if (options.searchField) { suggestions = suggestions.filter(data => { return options.searchField.some(x => (data[x] || '').toLowerCase().indexOf(queryLowerCase) !== -1); }); } else { suggestions = suggestions.filter(data => { return (data[options.valueField] || '').toLowerCase().indexOf(queryLowerCase) !== -1; }); } } if (typeof suggestions[0] !== 'object') { suggestions = suggestions.map(x => new Object({ value: x, data: x })); } else { suggestions = suggestions ? suggestions.map(x => { return { value: x[options.nameField], data: x }; }) : []; } callback({ suggestions: suggestions }); }; } if (!options.lookup) return; if (typeof options.lookup === 'function') { let lookupFn = options.lookup; return options.lookup = function (query, callback) { let queryLowerCase = (query || '').toLowerCase(); let suggestions = lookupFn.call(context, queryLowerCase) || []; if (typeof suggestions[0] !== 'object') { suggestions = suggestions.map(x => new Object({ value: x, data: x })); } else { suggestions = suggestions.map(x => { return { value: x[options.nameField], data: x }; }); } callback({ suggestions: suggestions }); }; } if (Array.isArray(options.lookup)) { let lookup = options.lookup; if (typeof lookup[0] !== 'object') { return options.lookup = lookup.map(x => new Object({ value: x, data: x })); } options.lookup = lookup.map(x => { return { value: x[options.nameField], data: x }; }); if (options.searchField) { options.lookupFilter = function (suggestion, originalQuery, queryLowerCase) { return options.searchField.some(x => (suggestion.data[x] || '').toLowerCase().indexOf(queryLowerCase) !== -1); }; } else { options.lookupFilter = function (suggestion, originalQuery, queryLowerCase) { return (suggestion.value || '').toLowerCase().indexOf(queryLowerCase) !== -1; }; } } else { throw '[autocomplete.lookup]必须为数组或函数'; } } FastVue.autocomplete = function (el, options, context) { !context && (context = this); el = FastVue.getDomElement(el) if (!el) throw '[FastVue.autocomplete]el is null or undefined:' + (options ? options.debugInfo : ''); let _el = el;//_el对应nameField options = Object.assign({ minChars: 0, autoSelectFirst: true }, options); !options.valueField && (options.valueField = options.nameField || 'value'); !options.nameField && (options.nameField = options.valueField); if (options.nameField !== options.valueField) { _el = el.cloneNode(true); _el.setAttribute('name', ''); _el.setAttribute('value', ''); _el.id && _el.setAttribute('id', _el.id + '_auto'); el.setAttribute('type', 'hidden'); if (!el.parentNode) { document.createElement("div").append(el); } el.parentNode.insertBefore(_el, el); el.dataset.autocompleteClone = _el; } __resetLookup(options, context); const onSelectFn = options.onSelect; options.onSelect = function (suggestion) { _el.dataset.select = '1'; if (options.nameField !== options.valueField) { el.value = suggestion.data[options.valueField]; } onSelectFn && onSelectFn(suggestion.data); }; $(_el).autocomplete('dispose'); $(_el).on('focus', function () { this.setAttribute("placeholder", this.value); this.value = ''; this.dataset.select = ''; }).on('blur', function () { !this.dataset.select && (this.value = this.getAttribute("placeholder")); this.setAttribute("placeholder", ''); }).on('keydown', function (event) { if ((event.which || event.keyCode) === 13) { event.preventDefault(); event.stopPropagation(); $(this).blur(); } }).autocomplete(options); return { setData(data) { if (options.nameField === options.valueField) { _el.value = (typeof data === 'object' ? data[options.nameField] : data) || ''; } else if (data) { el.value = data[options.valueField] || ''; _el.value = data[options.nameField] || ''; } else { _el.value = el.value = ''; } }, dispose() { $(_el).autocomplete('dispose'); $(_el).remove(); }, toggleDisabled(disabled) { _el.disabled = typeof disabled === 'undefined' ? !_el.disabled : !!disabled; }, clearCache() { $(_el).autocomplete('clearCache'); }, setOptions(newOptions) { $(_el).autocomplete('setOptions', Object.assign({}, options, newOptions)); } }; }; //静态dispose方法 FastVue.autocomplete.dispose = function (el) { (el instanceof jQuery) && (el = el.get(0)); if (!el) return; $(el).autocomplete('dispose'); el.dataset && $(el.dataset.autocompleteClone).autocomplete('dispose'); }; }(FastVue, jQuery)); //numberInput (function (window, $) { FastVue.numberInput = function (el, options) { let _el = typeof el === 'string' ? document.getElementById(el.replace(/^#/, '')) : el instanceof jQuery ? el[0] : el; if (!_el) { throw "[FastVue.numberInput]not found el:" + el; } let _ctrlV = false, _chnInput = -1; let _options = { append: '', precision: 2, negative: false, grouping: false, trimTailZeros: true }; function getValue() { let val = _el.value; if (val && val !== _options.append) { _options.append && _options.append !== '%' && _options.append !== '‱' && (val = val.replace(new RegExp(_options.append + "$"), '')); if (_options.stringMode) return val.replaceAll(",", ""); return FastVue.parseNumber(val, _options.percent); } return ''; } function setOptions(options) { options = Object.assign(_options, options); !options.append && (options.append = ''); let precision = parseInt(options.precision) || 0; options.precision = precision < 0 ? 0 : precision; let integerDigits = parseInt(options.integerDigits) || 0; options.integerDigits = integerDigits > 0 ? integerDigits : 0; options.negative = !!options.negative; _options = options; setValue(getValue()); } function setValue(value) { if (value || value === 0) { if (_options.stringMode && typeof value === 'string') { let text = value.trim().replaceAll(",", ""); if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text)) { _el.value = ''; return; } const negative = text.charAt(0) === '-'; text = text.replace(/^[+-]/, ''); const parts = text.split('.'); if (_options.grouping) parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); _el.value = (negative ? '-' : '') + parts.join('.') + _options.append; return; } let oval = parseFloat(value) || 0; if (_options.append === '%' || _options.percent == true) { oval *= 100; } else if (_options.append ==="‱") { oval *= 10000; } if (_options.precision > 0) { let str = main.formatNumber(oval, _options.precision, { grouping: _options.grouping }); if (_options.trimTailZeros !== false && typeof value === 'number' && str.endsWith("0") && str.includes('.')) { str = str.replace(/\.?0+$/, ''); } _el.value = str + _options.append; } else { let str = main.formatNumber(Math.floor(oval), 0, { grouping: _options.grouping }); _el.value = str + _options.append; } } else { _el.value = ''; } } function checkDotInput() { if (this.selectionStart >= this.value.length) { return _options.append && this.value.endsWith(_options.append) ? false : this.value.indexOf('.') < 0; } if (this.value.length - _options.append.length - this.selectionEnd > _options.precision) { return false; } return this.value.substring(0, this.selectionStart).indexOf('.') < 0 && this.value.substring(this.selectionEnd, this.value.length).indexOf('.') < 0; } function checkIntegerDigitsInput() { if (!_options.integerDigits) return true; let valueEnd = this.value.length; if (_options.append && this.value.endsWith(_options.append)) valueEnd -= _options.append.length; const dotIndex = this.value.indexOf('.'); const integerEnd = dotIndex < 0 ? valueEnd : dotIndex; if (this.selectionStart > integerEnd) return true; const selectionEnd = Math.min(this.selectionEnd, integerEnd); const integerText = this.value.substring(0, this.selectionStart) + this.value.substring(selectionEnd, integerEnd); return integerText.replace(/\D/g, '').length < _options.integerDigits; } function limitIntegerDigits(value) { if (!_options.integerDigits) return value; let digits = 0; let hasDot = false; let result = ''; for (let index = 0; index < value.length; index++) { const ch = value.charAt(index); if (ch === '.' || ch === '。') hasDot = true; if (ch >= '0' && ch <= '9' && !hasDot) { if (digits >= _options.integerDigits) continue; digits++; } result += ch; } return result; } function normalizeStringValue(value) { let text = String(value === null || value === undefined ? '' : value).trim().replaceAll(',', ''); if (!text) return ''; const negative = text.charAt(0) === '-'; text = text.replace(/^[+-]/, ''); if (!/^\d*(?:\.\d*)?$/.test(text)) return ''; let parts = text.split('.'); let integer = parts[0] || '0'; if (_options.integerDigits) integer = integer.substring(0, _options.integerDigits); const hasDot = parts.length > 1 && _options.precision > 0; const decimal = hasDot ? parts[1].substring(0, _options.precision) : ''; return (negative ? '-' : '') + integer + (hasDot ? '.' + decimal : ''); } function __keyHandle(event) { if (!event) return false; _chnInput = -1; this._enterFired = false; // 每次按键先清 Enter 标记;仅在回车分支重新置位,避免误触发 let charCode = event.which || event.keyCode; switch (charCode) { case 8: //break space return !_options.append || !this.value.endsWith(_options.append) || this.selectionStart < this.value.length - _options.append.length + 1; case 9: //tab case 35: //end case 36: //home case 37: //left arrow case 39: //right arrow case 42: //print case 116: //F5 return true; case 46: //del return !_options.append || !this.value.endsWith(_options.append) || this.selectionStart < this.value.length - _options.append.length; case 229: return _chnInput = this.selectionStart; } if (event.shiftKey) return false; switch (charCode) { case 109: //negative sign(-) return _options.negative && this.selectionStart < 1 && this.value.charAt(0) !== '-'; case 189: //negative sign(-_) return _options.negative && !event.shiftKey && this.selectionStart < 1 && this.value.charAt(0) !== '-'; case 110: //dot(.) return _options.precision > 0 && checkDotInput.call(this); case 190: //dot(.>) return _options.precision > 0 && !event.shiftKey && checkDotInput.call(this); case 13: //enter case 108: //enter this._enterFired = true; // 标记回车触发,供 __change 区分"回车"与"失焦" $(this).blur(); return true; case 67: //ctrl+c return event.ctrlKey; case 86: //ctrl+v return event.ctrlKey && (_ctrlV = true); default: if (charCode < 48 || charCode > 57 && (charCode < 96 || charCode > 105)) { return false; } if (!checkIntegerDigitsInput.call(this)) return false; if (this.selectionStart === 0) { return !this.value || this.value.charAt(this.selectionEnd) !== '-'; } if (_options.precision < 1) { return true; } if (this.selectionStart >= this.value.length && _options.append && this.value.endsWith(_options.append)) { return false; } if (this.selectionEnd > this.selectionStart) { return /\d/.test(this.value.substring(this.selectionStart, this.selectionEnd)); } else { let dotIndex = this.value.lastIndexOf('.', this.selectionStart - 1); return dotIndex < 0 || !/\d/.test(this.value[dotIndex + _options.precision]); } } } //处理中文字符输入 function __onChineseInput(chnPos) { if (this.value.length < chnPos) { chnPos = this.value.length - 1; } let ch = this.value.charCodeAt(chnPos); if (ch > 47 && ch < 58) { chnPos += 1; } else if (ch === 46 || ch === 12290) { if (_options.precision > 0 && !/\d/.test(this.value[this.selectionStart + _options.precision]) && this.value.lastIndexOf('.', chnPos - 1) < 0 && this.value.indexOf('.', this.selectionStart) < 0) { if (ch === 12290) { this.value = this.value.substring(0, chnPos) + '.' + this.value.substring(this.selectionStart); this.setSelectionRange(chnPos + 1, chnPos + 1); return; } chnPos += 1; } } else if (chnPos === 0 && ch === 45 && _options.negative && this.value[this.selectionStart] !== '-') { //负号 return; } else { for (; chnPos >= 0; chnPos--) { ch = this.value.charCodeAt(chnPos); if (ch === 46 || ch > 47 && ch < 58 || chnPos === 0 && ch === 45 && _options.negative) { chnPos += 1; break; } } } this.value = this.value.substring(0, chnPos) + this.value.substring(this.selectionStart); this.setSelectionRange(chnPos, chnPos); } //处理字符输入 function __onInput() { if (_ctrlV) { _ctrlV = false; // 粘贴进来的是显示值(可能带 %/‱ 后缀),先按 __change 同款口径解析为模型值再回显, // 避免 setValue 把显示值再乘以 100/10000(#EQD-5914 债券价格粘贴 ×100) let f = ''; this.value = limitIntegerDigits(this.value); if (this.value && this.value !== _options.append) { if (_options.stringMode) return setValue(normalizeStringValue(this.value)); f = parseFloat(this.value.replaceAll(",", "")) || 0; if (_options.append === '%' || _options.percent == true) f /= 100; else if (_options.append === '‱') f /= 10000; } return setValue(f); } if (_chnInput >= 0) { __onChineseInput.call(this, _chnInput); } if (_options.stringMode) { this.value = normalizeStringValue(this.value); return; } this.value = limitIntegerDigits(this.value); if (!_options.append || !this.value) return; let appended = true; if (this.value !== _options.append) { appended = this.value.endsWith(_options.append); } if (!appended) { let caretPos = this.selectionStart; this.value += _options.append; this.setSelectionRange(caretPos, caretPos); } else if (_options.negative && this.selectionStart === 2 && this.value[1] === '-') { this.value = this.value.substring(1); this.setSelectionRange(1, 1); } } //输入完成处理 function __change() { let f = ''; if (this.value && this.value !== _options.append) { if (_options.stringMode) { f = normalizeStringValue(this.value); setValue(f); } else { f = parseFloat(this.value.replaceAll(",", "")) || 0; if (_options.append === '%' || _options.percent == true) f /= 100; else if (_options.append === '‱') f /= 10000; } } let isEnter = !!this._enterFired; this._enterFired = false; _options.onchange && _options.onchange(f, this.value, isEnter); } //功能禁用 function __disable() { return false; } setOptions(options); $(_el).on('keydown', __keyHandle).on('dragenter', __disable).on('input', __onInput).on('change', __change); return { isEmpty() { return !_el.value || _el.value === _options.append; }, getValue: getValue, setValue: setValue, setOptions: setOptions, getOptions() { return Object.assign({}, _options); }, dispose() { $(_el).off(); } }; }; //静态dispose方法 FastVue.numberInput.dispose = function (el) { (el instanceof jQuery ? el : $(el)).off(); }; }(FastVue, jQuery)); //FastVue.vueNumberInput //数字输入组件(借助FastVue.numberInput) (function (global) { global.vueNumberInput = function () { return { props: { value: { type: [Number, String], default: '' }, format: { type: Object, default: { append: '', precision: 2, negative: false, grouping: false, percent: false } }, disabled: { type: Boolean }, ret_type: { type: Number, default: 0 } }, data() { return { inputctrl: null,init:true}; }, mounted() { let self = this; let options = FastVue.extend({ onchange: this.onchange }, this.format); this.inputctrl = FastVue.numberInput(this.$el, options); this.inputctrl.setValue(this.value); // 按键时 emit 'keydown' 事件,供父组件监听按键输入(交互约定3) // .native 修饰符在此组件上不可靠(jQuery 在 mounted 中重新绑定了 keydown), // 改为在组件内部 addEventListener 并 emit,确保父组件能收到按键事件 this.$el.addEventListener('keydown', function (e) { self.$emit('keydown', e); }); }, methods: { onchange(value, text, isEnter) { this.init = false; const result = this.ret_type === 1 ? text : value; this.$emit('input', result); if (isEnter) this.$emit('enter', result); } }, watch: { value(val, old) { let val1 = ''; switch (typeof val) { case 'number': val1 = val || 0; break; case 'string': if (this.format && this.format.stringMode) { val1 = val; break; } if (val) { val1 = parseFloat(val) || 0; if (val1 && (val.trimEnd().endsWith('%') || (this.format.percent && !this.init))) { val1 /= 100; } else if (val1 && (val.trimEnd().endsWith('‱'))){ val1 /= 10000; } } break; } if (!this.inputctrl.isEmpty()) { let val2 = this.inputctrl.getValue(); if (Math.abs(val1 - val2) < 1e-10) return; } this.$nextTick(function () { this.inputctrl.setValue(val1); }); }, format: { deep: true, handler(val) { this.inputctrl.setOptions(val); this.$nextTick(function () { this.inputctrl.setValue(this.value); }); } } }, destroyed() { FastVue.numberInput.dispose(this.$el); }, template: '' }; }; }(window.FastVue));