新购到货管理/领料申请-类型规格-优化
This commit is contained in:
parent
a0519e57fa
commit
fa474c8a07
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": true,
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "lf",
|
||||
"useTabs": false,
|
||||
"trailingComma": "none",
|
||||
"bracketSameLine": false,
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"singleAttributePerLine": false,
|
||||
"importStatement": "none"
|
||||
}
|
||||
|
|
@ -91,20 +91,57 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item label="类型规格" prop="deviceType">
|
||||
<el-cascader
|
||||
:key="propsKey"
|
||||
v-model="deviceType"
|
||||
:show-all-levels="false"
|
||||
:options="equipmentTypeList"
|
||||
:props="deviceTypeTreeProps"
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="15">
|
||||
<el-select
|
||||
ref="typeSelect"
|
||||
v-model="tempDeviceType"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请输入类型规格"
|
||||
style="width: 500px"
|
||||
@change="handleTypeChange"
|
||||
clearable
|
||||
collapse-tags
|
||||
style="width: 240px"
|
||||
placeholder="请选择规格型号"
|
||||
ref="deviceTypeCascader"
|
||||
popper-class="popper-select"
|
||||
@change="deviceTypeChange"
|
||||
></el-cascader>
|
||||
:filter-method="handleSearchImpl"
|
||||
:popper-class="'type-select-dropdown'"
|
||||
:popper-append-to-body="false"
|
||||
@visible-change="handleVisibleChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in filteredOptions"
|
||||
:key="item.typeId"
|
||||
:label="item.fullPath"
|
||||
:value="item.typeId"
|
||||
:data-key="item.typeId"
|
||||
>
|
||||
<span v-html="highlightText(item.fullPath, searchKeyword)"></span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">
|
||||
库存:{{ item.storageNum }}
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-input
|
||||
ref="searchInput"
|
||||
v-model="searchKeyword"
|
||||
placeholder="输入类型规格高亮搜索"
|
||||
prefix-icon="el-icon-search"
|
||||
clearable
|
||||
style="width: 300px"
|
||||
@input="handleHighlightSearch"
|
||||
@focus="handleSearchFocus"
|
||||
@click.native="handleSearchClick"
|
||||
>
|
||||
<template slot="append">
|
||||
<span v-if="matchedOptions.length" style="margin: 0 5px">
|
||||
{{ currentMatchIndex + 1 }}/{{ matchedOptions.length }}
|
||||
</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
|
|
@ -340,9 +377,9 @@ export default {
|
|||
deviceTypeTreeProps: {
|
||||
children: "children",
|
||||
label: "typeName",
|
||||
// multiple: false,
|
||||
value: "typeId",
|
||||
multiple: true,
|
||||
emitPath: true
|
||||
},
|
||||
deviceType: [],
|
||||
propsKey: 1000,
|
||||
|
|
@ -350,6 +387,23 @@ export default {
|
|||
unitTemp: undefined,
|
||||
agreementId: undefined,
|
||||
// taxRate:0,
|
||||
flattenOptions: [], // 扁平化后的选项数据
|
||||
typePopoverVisible: false,
|
||||
typeOptions: [], // 类型选项
|
||||
allTypeList: [], // 所有类型数据
|
||||
flattenTypeOptions: [], // 扁平化后的选项数据(缓存所有选项)
|
||||
typeGroups: [], // 分组后的类型选项
|
||||
typeGroupsBackup: [], // 备份原始分组数据,用于搜索
|
||||
typeMap: new Map(), // 用于快速查找类型数据
|
||||
tempDeviceType: [], // 临时选中值
|
||||
filteredOptions: [], // 过滤后的选项(用于显示)
|
||||
maxShowOptions: 100, // 最大显示选项数
|
||||
searchTimer: null, // 用于自定义防抖
|
||||
searchKeyword: '', // 高亮搜索关键字
|
||||
currentMatchIndex: -1, // 当前匹配项索引
|
||||
matchedOptions: [], // 匹配的选项
|
||||
keepSelectOpen: false, // 控制下拉框是否保持打开
|
||||
isSearching: false, // 添加搜索状态标记
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -363,7 +417,6 @@ export default {
|
|||
};
|
||||
},
|
||||
},
|
||||
watch: {},
|
||||
mounted() {
|
||||
this.projectInfoList();//单位工程下拉选
|
||||
this.equipmentType();//机具类型下拉选
|
||||
|
|
@ -452,84 +505,95 @@ export default {
|
|||
equipmentType() {
|
||||
equipmentTypeTree().then((response) => {
|
||||
this.equipmentTypeList = response.data;
|
||||
this.equipmentTypeList.forEach((item, index) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
item.children.forEach((item2, index2) => {
|
||||
if (item2.children && item2.children.length > 0) {
|
||||
item2.children.forEach((item3) => {
|
||||
if (item3.children && item3.children.length > 0) {
|
||||
item3.children.forEach((item4) => {
|
||||
item4.maTypeName = item3.typeName;
|
||||
item4.specificationType = item4.typeName;
|
||||
// this.$set(item4, "purchaseTaxPrice", 0);
|
||||
// this.$set(item4, "purchasePrice", 0);
|
||||
});
|
||||
// 处理并扁平化所有类型数据
|
||||
this.flattenTypeOptions = this.processTypeData(response.data);
|
||||
// 初始显示所有选项
|
||||
this.filteredOptions = [...this.flattenTypeOptions];
|
||||
|
||||
// 反显选中数据
|
||||
if (this.equipmentList.length > 0) {
|
||||
this.deviceType = this.equipmentList.map(item => item.typeId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
//反显
|
||||
let selectList = [];
|
||||
this.equipmentList.forEach((e) => {
|
||||
selectList.push(
|
||||
this.getParentsById(this.equipmentTypeList, e.typeId)
|
||||
);
|
||||
});
|
||||
this.deviceType = selectList;
|
||||
});
|
||||
},
|
||||
//添加机具类型
|
||||
deviceTypeChange(val) {
|
||||
const deviceTypeList = this.$refs.deviceTypeCascader.getCheckedNodes();
|
||||
let tempList = [];
|
||||
if (val.length > 0) {
|
||||
const items = val.map((e) => {
|
||||
return e[3];
|
||||
// 处理类型数据
|
||||
processTypeData(data) {
|
||||
const result = [];
|
||||
|
||||
const traverse = (node, parents = []) => {
|
||||
const path = [...parents, node.typeName];
|
||||
|
||||
if (!node.children || node.children.length === 0) {
|
||||
result.push({
|
||||
typeId: node.typeId,
|
||||
typeName: node.typeName,
|
||||
fullPath: path.join(' / '),
|
||||
searchKey: path.join('').toLowerCase(),
|
||||
storageNum: node.storageNum || 0,
|
||||
maTypeName: parents[parents.length - 1] || '',
|
||||
specificationType: node.typeName,
|
||||
unitName: node.unitName,
|
||||
unitValue: node.unitValue
|
||||
});
|
||||
for (let i of items) {
|
||||
for (let z of deviceTypeList) {
|
||||
if (z.data.typeId === i) {
|
||||
const obj = JSON.parse(JSON.stringify(z.data));
|
||||
obj.preNum = 0;
|
||||
if(obj.storageNum<=0){
|
||||
let index = this.deviceType.length;
|
||||
if(index==1){
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
node.children.forEach(child => traverse(child, path));
|
||||
}
|
||||
};
|
||||
|
||||
data.forEach(node => traverse(node));
|
||||
return result;
|
||||
},
|
||||
// 搜索处理函数
|
||||
handleSearchImpl(query) {
|
||||
if (!query) {
|
||||
this.filteredOptions = [...this.flattenTypeOptions];
|
||||
return;
|
||||
}
|
||||
|
||||
const lowercaseQuery = query.toLowerCase();
|
||||
this.filteredOptions = this.flattenTypeOptions.filter(item =>
|
||||
item.searchKey.includes(lowercaseQuery) ||
|
||||
item.fullPath.toLowerCase().includes(lowercaseQuery)
|
||||
);
|
||||
},
|
||||
// 选择变化处理
|
||||
handleTypeChange(val) {
|
||||
if (!val || val.length === 0) return;
|
||||
|
||||
// 获取新选中的项
|
||||
const lastSelected = val[val.length - 1];
|
||||
const typeData = this.flattenTypeOptions.find(item => item.typeId === lastSelected);
|
||||
|
||||
if (typeData) {
|
||||
if (this.equipmentList.some(item => item.typeId === lastSelected)) {
|
||||
this.$message({
|
||||
message: `${typeData.typeName} 已添加到列表中`,
|
||||
type: 'warning'
|
||||
});
|
||||
this.tempDeviceType = this.tempDeviceType.filter(id => id !== lastSelected);
|
||||
} else if (typeData.storageNum <= 0) {
|
||||
this.$message.error("所选物资规格类型暂时无库存,无法申请!");
|
||||
this.tempDeviceType = this.tempDeviceType.filter(id => id !== lastSelected);
|
||||
} else {
|
||||
// 将新项添加到数组开头,实现倒序
|
||||
this.equipmentList.unshift({
|
||||
...typeData,
|
||||
preNum: 0
|
||||
});
|
||||
this.deviceType.push(lastSelected);
|
||||
this.$message({
|
||||
message: `已添加 ${typeData.typeName}`,
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 清空临时选中值
|
||||
this.$nextTick(() => {
|
||||
this.deviceType=[]; // 可选,如果你想要重置选中状态
|
||||
this.tempDeviceType = [];
|
||||
});
|
||||
}else{
|
||||
this.deviceType=this.deviceType.splice(0,1)
|
||||
}
|
||||
this.$modal.msgError("所选物资规格类型暂时无库存,无法申请!");
|
||||
}else{
|
||||
tempList.push(obj);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const newDataListNew = [...this.equipmentList, ...tempList];
|
||||
const map = new Map();
|
||||
for (let item of newDataListNew) {
|
||||
if (!map.has(item.typeId)) {
|
||||
map.set(item.typeId, item);
|
||||
}
|
||||
}
|
||||
const newArray = [...map.values()];
|
||||
let newArray_array = [];
|
||||
items.forEach((e) => {
|
||||
newArray.forEach((j) => {
|
||||
if (e == j.typeId) {
|
||||
newArray_array.push(j);
|
||||
}
|
||||
});
|
||||
});
|
||||
this.equipmentList = newArray_array;
|
||||
} else {
|
||||
this.equipmentList = [];
|
||||
}
|
||||
},
|
||||
//获取任务详情--- 编辑回显数据
|
||||
async getTaskInfo() {
|
||||
|
|
@ -775,42 +839,16 @@ export default {
|
|||
this.dialogVisible = true;
|
||||
}
|
||||
},
|
||||
//树结构数据获取父
|
||||
getParentsById(list, id) {
|
||||
for (let i in list) {
|
||||
if (list[i].typeId == id) {
|
||||
//查询到就返回该数组对象的value
|
||||
return [list[i].typeId];
|
||||
}
|
||||
if (list[i].children) {
|
||||
let node = this.getParentsById(list[i].children, id);
|
||||
if (node !== undefined) {
|
||||
//查询到把父节把父节点加到数组前面
|
||||
node.unshift(list[i].typeId);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
console.log(row)
|
||||
this.$modal
|
||||
.confirm("是否确认删除所选择的数据项?")
|
||||
.then(() => {
|
||||
this.deviceType.forEach((e, index) => {
|
||||
console.log("e", e);
|
||||
if (e[3] === row.typeId) {
|
||||
this.deviceType.splice(index, 1);
|
||||
this.propsKey++;
|
||||
}
|
||||
});
|
||||
console.log(this.equipmentList)
|
||||
this.equipmentList.forEach((item, index) => {
|
||||
if (item.typeId == row.typeId) {
|
||||
this.equipmentList.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.equipmentList = this.equipmentList.filter(
|
||||
item => item.typeId !== row.typeId
|
||||
);
|
||||
// 更新实际存储的选中值
|
||||
this.deviceType = this.equipmentList.map(item => item.typeId);
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
|
@ -822,7 +860,88 @@ export default {
|
|||
`新购到货详情_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
},
|
||||
// 高亮文本
|
||||
highlightText(text, keyword) {
|
||||
if (!keyword) return text;
|
||||
const reg = new RegExp(keyword, 'gi');
|
||||
return text.replace(reg, match => `<span class="highlight-text">${match}</span>`);
|
||||
},
|
||||
|
||||
// 处理下拉框可见性变化
|
||||
handleVisibleChange(visible) {
|
||||
if (!visible && this.keepSelectOpen && !this.isSearching) {
|
||||
// 只有在非搜索状态下才重新打开下拉框
|
||||
this.$nextTick(() => {
|
||||
this.$refs.typeSelect.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 处理搜索框点击
|
||||
handleSearchClick() {
|
||||
this.isSearching = true;
|
||||
this.keepSelectOpen = true;
|
||||
// 确保下拉框打开
|
||||
this.$refs.typeSelect.focus();
|
||||
// 立即将焦点返回给搜索框
|
||||
this.$nextTick(() => {
|
||||
this.$refs.searchInput.focus();
|
||||
});
|
||||
},
|
||||
|
||||
// 处理搜索框获得焦点
|
||||
handleSearchFocus() {
|
||||
this.isSearching = true;
|
||||
if (!this.$refs.typeSelect.visible) {
|
||||
this.$refs.typeSelect.focus();
|
||||
this.$nextTick(() => {
|
||||
this.$refs.searchInput.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 高亮搜索处理
|
||||
handleHighlightSearch() {
|
||||
this.isSearching = true;
|
||||
this.keepSelectOpen = true;
|
||||
|
||||
if (!this.searchKeyword) {
|
||||
this.currentMatchIndex = -1;
|
||||
this.matchedOptions = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到所有匹配项
|
||||
this.matchedOptions = this.filteredOptions.filter(item =>
|
||||
item.fullPath.toLowerCase().includes(this.searchKeyword.toLowerCase())
|
||||
);
|
||||
|
||||
if (this.matchedOptions.length > 0) {
|
||||
this.currentMatchIndex = 0;
|
||||
this.$nextTick(() => {
|
||||
this.scrollToMatch();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 滚动到匹配项
|
||||
scrollToMatch() {
|
||||
if (this.currentMatchIndex === -1 || !this.matchedOptions.length) return;
|
||||
|
||||
const option = this.matchedOptions[this.currentMatchIndex];
|
||||
const selectDom = this.$el.querySelector('.el-select-dropdown__wrap');
|
||||
const optionDom = selectDom?.querySelector(`[data-key="${option.typeId}"]`);
|
||||
|
||||
if (optionDom) {
|
||||
optionDom.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
||||
}
|
||||
},
|
||||
},
|
||||
// 添加组件销毁时的清理
|
||||
beforeDestroy() {
|
||||
this.keepSelectOpen = false;
|
||||
this.isSearching = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
|
@ -835,4 +954,75 @@ export default {
|
|||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.el-icon-arrow-down.is-reverse {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
|
||||
.el-tree {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.highlight-text {
|
||||
background-color: #ffd04b;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
::v-deep .el-input-group__append {
|
||||
padding: 0;
|
||||
.el-button {
|
||||
padding: 0 10px;
|
||||
border: none;
|
||||
height: 100%;
|
||||
&:first-child {
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
&[disabled] {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.type-select-dropdown {
|
||||
.el-select-dropdown__wrap {
|
||||
max-height: 400px !important;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item {
|
||||
height: auto;
|
||||
padding: 8px 20px;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.highlight-text {
|
||||
background-color: #ffd04b;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.el-input-group__append {
|
||||
padding: 0;
|
||||
.el-button {
|
||||
padding: 0 10px;
|
||||
border: none;
|
||||
height: 100%;
|
||||
&:first-child {
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
&[disabled] {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue