refactor(组件): 重构发货单明细表格为独立组件 feat(组件): 添加发货单打印预览组件 fix(登录页): 移除默认用户名和密码 style(钢卷面板): 添加显示控制属性并优化布局 chore: 添加vitest测试配置和依赖
178 lines
4.8 KiB
Vue
178 lines
4.8 KiB
Vue
<template>
|
||
<el-autocomplete class="inline-input" v-model="inputValue" :fetch-suggestions="querySearch" :placeholder="placeholder"
|
||
:trigger-on-focus="triggerMode === 'focus'" @select="handleSelect" @change="handleInputChange"
|
||
clearable></el-autocomplete>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'LocalStorageAutocomplete',
|
||
props: {
|
||
// localStorage 存储键名,必填
|
||
value: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
storageKey: {
|
||
type: String,
|
||
required: true,
|
||
validator: (value) => value.trim() !== '' // 校验非空
|
||
},
|
||
// 输入框占位符,默认“请输入内容”
|
||
placeholder: {
|
||
type: String,
|
||
default: '请输入内容'
|
||
},
|
||
// // 当localstorage中不存在缓存时,自动创建一个数组作为默认值
|
||
// autoCreateDefault: {
|
||
// type: Array,
|
||
// default: () => []
|
||
// },
|
||
// 最大缓存数量,默认100条
|
||
maxCacheCount: {
|
||
type: Number,
|
||
default: 100,
|
||
validator: (value) => value > 0 // 校验正整数
|
||
},
|
||
// 匹配规则:start-前缀匹配,contain-包含匹配,默认前缀匹配
|
||
matchRule: {
|
||
type: String,
|
||
default: 'start',
|
||
validator: (value) => ['start', 'contain'].includes(value)
|
||
},
|
||
// 触发模式:focus-激活即列出,input-输入后匹配,默认focus
|
||
triggerMode: {
|
||
type: String,
|
||
default: 'focus',
|
||
validator: (value) => ['focus', 'input'].includes(value)
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
inputValue: '',
|
||
// 缓存历史列表(格式:[{value: 'xxx'}, ...])
|
||
historyList: []
|
||
};
|
||
},
|
||
computed: {
|
||
innerKey() {
|
||
if (!this.storageKey) {
|
||
throw new Error('storageKey is required');
|
||
}
|
||
return 'mono-' + this.storageKey;
|
||
}
|
||
},
|
||
watch: {
|
||
value: {
|
||
handler(newVal, oldVal) {
|
||
if (newVal !== oldVal) {
|
||
this.inputValue = newVal;
|
||
}
|
||
},
|
||
immediate: true
|
||
}
|
||
},
|
||
mounted() {
|
||
// 初始化时从localStorage读取历史记录
|
||
this.initHistoryList();
|
||
},
|
||
methods: {
|
||
/**
|
||
* 从localStorage初始化历史列表
|
||
*/
|
||
initHistoryList() {
|
||
const storedData = localStorage.getItem(this.innerKey);
|
||
this.historyList = storedData ? JSON.parse(storedData) : [];
|
||
},
|
||
|
||
/**
|
||
* 搜索查询方法(Element UI autocomplete 要求的格式)
|
||
* @param {string} queryString - 输入的查询字符串
|
||
* @param {function} cb - 回调函数,返回匹配结果
|
||
*/
|
||
querySearch(queryString, cb) {
|
||
let results = [...this.historyList]; // 深拷贝避免修改原数组
|
||
|
||
// 根据查询字符串过滤结果
|
||
if (queryString) {
|
||
const lowerQuery = queryString.toLowerCase();
|
||
results = results.filter(item => {
|
||
const lowerValue = item.value.toLowerCase();
|
||
// 前缀匹配或包含匹配
|
||
return this.matchRule === 'start'
|
||
? lowerValue.indexOf(lowerQuery) === 0
|
||
: lowerValue.includes(lowerQuery);
|
||
});
|
||
}
|
||
|
||
cb(results); // 回调返回匹配结果
|
||
},
|
||
|
||
/**
|
||
* 选中建议项时的处理
|
||
* @param {object} item - 选中的项 {value: 'xxx'}
|
||
*/
|
||
handleSelect(item) {
|
||
this.cacheInputValue(item.value);
|
||
this.inputValue = item.value;
|
||
// 触发自定义事件,通知父组件选中结果
|
||
this.$emit('input', item.value);
|
||
},
|
||
|
||
/**
|
||
* 输入框内容变化时的处理(比如手动输入后回车/失焦)
|
||
* @param {string} value - 输入框的值
|
||
*/
|
||
handleInputChange(value) {
|
||
if (value && value.trim() !== '') {
|
||
this.cacheInputValue(value.trim());
|
||
// 触发自定义事件,通知父组件输入结果
|
||
this.$emit('input', value.trim());
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 缓存输入值到localStorage
|
||
* @param {string} value - 要缓存的值
|
||
*/
|
||
cacheInputValue(value) {
|
||
// 去重:如果已存在则移除原记录(保证最新输入在最前面)
|
||
this.historyList = this.historyList.filter(item => item.value !== value);
|
||
// 添加新记录到头部
|
||
this.historyList.unshift({ value });
|
||
// 限制最大缓存数量
|
||
if (this.historyList.length > this.maxCacheCount) {
|
||
this.historyList = this.historyList.slice(0, this.maxCacheCount);
|
||
}
|
||
// 保存到localStorage
|
||
localStorage.setItem(this.innerKey, JSON.stringify(this.historyList));
|
||
},
|
||
|
||
/**
|
||
* 清空历史记录(提供给父组件调用的方法)
|
||
*/
|
||
clearHistory() {
|
||
this.historyList = [];
|
||
localStorage.removeItem(this.innerKey);
|
||
this.inputValue = '';
|
||
this.$emit('clear');
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.demo-autocomplete {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.sub-title {
|
||
margin-bottom: 10px;
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.inline-input {
|
||
width: 100%;
|
||
}
|
||
</style> |