14 changed files with 964 additions and 156 deletions
-
24src/api/archivesManage/fileImport.js
-
12src/api/storeManage/deviceManage/device.js
-
218src/views/archivesManage/fileImport/dataImport/index.vue
-
60src/views/archivesManage/fileImport/importLog/index.vue
-
1src/views/archivesManage/fileImport/index.vue
-
139src/views/archivesManage/fileImport/module/EditableCell.vue
-
122src/views/archivesManage/fileImport/module/detail.vue
-
202src/views/archivesManage/fileImport/module/listPre-edit.vue
-
282src/views/archivesManage/fileImport/module/listPre.vue
-
8src/views/components/AccessDoor.vue
-
8src/views/components/WarehouseWarning.vue
-
11src/views/environmentalScreen/index.vue
-
4src/views/home.vue
-
19src/views/storeManage/deviceManage/module/deviceDetail.vue
@ -0,0 +1,139 @@ |
|||
<template> |
|||
<div class="editable-cell-wrapper"> |
|||
<!-- 显示模式 --> |
|||
<span |
|||
v-if="!isEditing" |
|||
class="cell-text" |
|||
@dblclick="startEditing" |
|||
> |
|||
{{ value }} |
|||
</span> |
|||
|
|||
<!-- 编辑模式 --> |
|||
<el-input |
|||
v-else |
|||
ref="editInput" |
|||
:value="value" |
|||
size="mini" |
|||
class="cell-input" |
|||
@input="handleInput" |
|||
@blur="finishEditing" |
|||
@keyup.enter.native="finishEditing" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'EditableCell', |
|||
props: { |
|||
// 单元格的原始值 |
|||
value: { |
|||
type: [String, Number, Boolean, null], |
|||
default: null |
|||
}, |
|||
// 用于唯一标识行和列,避免同时编辑多个单元格 |
|||
cellKey: { |
|||
type: String, |
|||
required: true |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
// 组件内部的编辑状态 |
|||
isEditing: false, |
|||
// 存储当前正在编辑的单元格的 cellKey |
|||
editingCellKey: '' |
|||
} |
|||
}, |
|||
beforeDestroy() { |
|||
// 组件销毁时,移除全局事件监听 |
|||
this.$root.$off('editable-cell:cancel-edit', this.handleCancelEdit) |
|||
}, |
|||
methods: { |
|||
/** |
|||
* 开始编辑 |
|||
*/ |
|||
startEditing() { |
|||
// 确保一次只编辑一个单元格 |
|||
if (this.editingCellKey && this.editingCellKey !== this.cellKey) { |
|||
this.$root.$emit('editable-cell:cancel-edit', this.editingCellKey) |
|||
} |
|||
this.isEditing = true |
|||
this.editingCellKey = this.cellKey |
|||
|
|||
// 监听全局事件,用于取消其他单元格的编辑 |
|||
this.$root.$on('editable-cell:cancel-edit', this.handleCancelEdit) |
|||
|
|||
// 确保输入框渲染后再聚焦 |
|||
this.$nextTick(() => { |
|||
this.$refs.editInput.focus() |
|||
}) |
|||
}, |
|||
|
|||
/** |
|||
* 处理输入 |
|||
* @param {string} newValue - 输入框的新值 |
|||
*/ |
|||
handleInput(newValue) { |
|||
// 使用 .trim() 去除首尾空格 |
|||
// 这可以确保 null、undefined、'' 都被视为空值 |
|||
const trimmedValue = newValue.trim() || null |
|||
// 通过 'input' 事件将新值传递给父组件 |
|||
this.$emit('input', trimmedValue) |
|||
}, |
|||
|
|||
/** |
|||
* 结束编辑(失去焦点或按回车) |
|||
*/ |
|||
finishEditing() { |
|||
this.isEditing = false |
|||
this.editingCellKey = '' |
|||
// 移除事件监听,防止内存泄漏 |
|||
this.$root.$off('editable-cell:cancel-edit', this.handleCancelEdit) |
|||
// 通知父组件编辑已完成 |
|||
this.$emit('change') |
|||
}, |
|||
|
|||
/** |
|||
* 处理来自其他单元格的取消编辑请求 |
|||
* @param {string} key - 请求取消编辑的单元格的 cellKey |
|||
*/ |
|||
handleCancelEdit(key) { |
|||
if (key === this.cellKey) { |
|||
this.isEditing = false |
|||
this.editingCellKey = '' |
|||
this.$root.$off('editable-cell:cancel-edit', this.handleCancelEdit) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.editable-cell-wrapper { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
.cell-text { |
|||
display: block; |
|||
width: 100%; |
|||
height: 100%; |
|||
padding: 0 5px; /* 增加内边距,方便点击 */ |
|||
cursor: pointer; |
|||
word-break: break-all; |
|||
} |
|||
.cell-text:hover { |
|||
background-color: rgba(51, 156, 255, 0.1); |
|||
} |
|||
.cell-input { |
|||
padding: 0; |
|||
margin: 0; |
|||
} |
|||
/* 让 el-input 完全填充单元格 */ |
|||
::v-deep .cell-input .el-input__inner { |
|||
padding: 0 5px; |
|||
height: 28px; /* 与表格行高对齐 */ |
|||
line-height: 28px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,202 @@ |
|||
<template> |
|||
<div class="import-detail-container"> |
|||
<ul class="import-tab"> |
|||
<li class="active">文件</li> |
|||
</ul> |
|||
<el-table |
|||
ref="table" |
|||
:data="tableData" |
|||
style="width: 100%" |
|||
height="calc(100vh - 500px)" |
|||
row-key="id" |
|||
@row-click="clickRowHandler" |
|||
@selection-change="selectionChangeHandler" |
|||
> |
|||
<el-table-column prop="status" label="状态" align="center" width="80px"> |
|||
<template slot-scope="scope"> |
|||
<el-tag v-if="scope.row.status===1" type="success">导入</el-tag> |
|||
<el-tag v-if="scope.row.status===2" type="warning">覆盖</el-tag> |
|||
<el-tag v-if="scope.row.status===3">跳过</el-tag> |
|||
<el-tag v-if="scope.row.status===4" type="danger">失败</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="序号" width="60" align="center" type="index" /> |
|||
|
|||
<el-table-column prop="maintitle" label="姓名" align="center" show-overflow-tooltip> |
|||
<template slot-scope="scope"> |
|||
<editable-cell |
|||
:value="scope.row.maintitle" |
|||
:cell-key="`${scope.row.id}-maintitle`" |
|||
@input="(val) => { scope.row.maintitle = val }" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="idType" label="身份类别" align="center" show-overflow-tooltip> |
|||
<template slot-scope="scope"> |
|||
<editable-cell |
|||
:value="scope.row.idType" |
|||
:cell-key="`${scope.row.id}-idType`" |
|||
@input="(val) => { scope.row.idType = val }" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="idNumber" label="公民身份证号" align="center" show-overflow-tooltip> |
|||
<template slot-scope="scope"> |
|||
<editable-cell |
|||
:value="scope.row.idNumber" |
|||
:cell-key="`${scope.row.id}-idNumber`" |
|||
@input="(val) => { scope.row.idNumber = val }" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="nativePlace" label="籍贯" align="center" show-overflow-tooltip> |
|||
<template slot-scope="scope"> |
|||
<editable-cell |
|||
:value="scope.row.nativePlace" |
|||
:cell-key="`${scope.row.id}-nativePlace`" |
|||
@input="(val) => { scope.row.nativePlace = val }" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="archiveNo" label="证件号" align="center" show-overflow-tooltip> |
|||
<template slot-scope="scope"> |
|||
<editable-cell |
|||
:value="scope.row.archiveNo" |
|||
:cell-key="`${scope.row.id}-archiveNo`" |
|||
@input="(val) => { scope.row.archiveNo = val }" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<!--分页组件--> |
|||
<el-pagination |
|||
:current-page="page.page" |
|||
:total="page.total" |
|||
:page-size="page.size" |
|||
:pager-count="5" |
|||
layout="total, prev, pager, next, sizes" |
|||
@size-change="handleSizeChange" |
|||
@current-change="handleCurrentPage" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import crudFileImport from '@/api/archivesManage/fileImport' |
|||
import EditableCell from './EditableCell.vue' |
|||
|
|||
export default { |
|||
name: 'ListPre', |
|||
components: { EditableCell }, |
|||
props: { |
|||
djnum: { type: Number, default: 0 }, |
|||
hpId: { type: String, default: '' }, |
|||
initialTableData: { type: Array, default: () => [] }, |
|||
initialTotal: { type: Number, default: 0 } |
|||
}, |
|||
data() { |
|||
return { |
|||
tableData: [], |
|||
page: { |
|||
page: 1, |
|||
size: 10, |
|||
total: 0 |
|||
} |
|||
} |
|||
}, |
|||
watch: { |
|||
initialTableData(newVal) { |
|||
if (newVal) { |
|||
this.tableData = newVal |
|||
} |
|||
}, |
|||
initialTotal(newVal) { |
|||
if (newVal !== undefined) { |
|||
this.page.total = newVal |
|||
} |
|||
} |
|||
}, |
|||
created() { |
|||
this.tableData = this.initialTableData |
|||
this.page.total = this.initialTotal |
|||
}, |
|||
methods: { |
|||
getList() { |
|||
crudFileImport.FetchHpArchivesDetailsByHpId({ |
|||
page: this.page.page - 1, |
|||
size: this.page.size, |
|||
hpId: this.hpId, |
|||
isError: false |
|||
}).then(res => { |
|||
this.tableData = res.content |
|||
this.page.total = res.totalElements |
|||
}) |
|||
this.doLayout() |
|||
}, |
|||
clickRowHandler(row) { |
|||
this.$refs.table.toggleRowSelection(row) |
|||
}, |
|||
selectionChangeHandler(val) { |
|||
this.selections = val |
|||
}, |
|||
handleSizeChange(size) { |
|||
this.page.size = size |
|||
this.page.page = 1 |
|||
this.getList() |
|||
}, |
|||
handleCurrentPage(val) { |
|||
this.page.page = val |
|||
this.getList() |
|||
}, |
|||
doLayout() { |
|||
this.$nextTick(() => { |
|||
this.$refs.table.doLayout() |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang='scss' scoped> |
|||
/* 你的样式代码保持不变 */ |
|||
.import-detail-container { |
|||
position: relative; |
|||
} |
|||
.import-tab { |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
margin-bottom: 10px; |
|||
li { |
|||
padding: 10px 20px 14px 20px; |
|||
font-size: 16px; |
|||
color: #339CFF; |
|||
cursor: pointer; |
|||
&.active { |
|||
position: relative; |
|||
color: #fff; |
|||
&::after { |
|||
content: ""; |
|||
position: absolute; |
|||
left: 0; |
|||
bottom: -1px; |
|||
width: 100%; |
|||
height: 3px; |
|||
border-radius: 3px; |
|||
background-color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.el-tag.el-tag--success { |
|||
color: #1aae93; |
|||
border-color: #1aae93; |
|||
background-color: transparent !important; |
|||
} |
|||
/* 让单元格内容垂直居中 */ |
|||
::v-deep .el-table .cell { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; /* 针对 align="center" 的列 */ |
|||
height: 100%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,282 @@ |
|||
<template> |
|||
<div class="import-detail-container"> |
|||
<div v-if="isLogOrPreview === 'log'" class="import-data-number"> |
|||
<p>成功读取数据:<span style="color:#339cff">{{ readData === null ? 0 : readData }}</span> 条</p> |
|||
<p>成功导入数据:<span style="color: #1AAE93;">{{ importData === null ? 0 : importData }}</span> 条</p> |
|||
<p>跳过数据:<span style="color: rgb(246,81,99);">{{ skipData === null ? 0 : skipData }}</span> 条</p> |
|||
<p>覆盖数据:<span style="color: rgb(246,81,99);">{{ coverData === null ? 0 : coverData }}</span> 条</p> |
|||
<p>导入失败数据:<span style="color: rgb(246,81,99);">{{ errorData === null ? 0 : errorData }}</span> 条</p> |
|||
|
|||
</div> |
|||
<div class="import-tab"> |
|||
<p class="active">文件</p> |
|||
<el-checkbox v-if="isLogOrPreview !== 'log'" v-model="isData" style="margin-left: 20px; color: #fff;" @change="changeDataChecked">仅显示问题数据</el-checkbox> |
|||
</div> |
|||
<el-table |
|||
ref="table" |
|||
:data="tableData" |
|||
style="width: 100%" |
|||
height="calc(100vh - 500px)" |
|||
@row-click="clickRowHandler" |
|||
@selection-change="selectionChangeHandler" |
|||
> |
|||
<el-table-column v-if="isLogOrPreview !== 'log'" prop="status" label="状态" align="center" width="120px"> |
|||
<template slot-scope="scope"> |
|||
<!-- 状态为1的行显示为"导入" --> |
|||
<el-tag v-if="scope.row.status === 1" type="success">导入</el-tag> |
|||
<!-- 状态为3且有空值的行显示为"跳过" --> |
|||
<el-tag v-else-if="scope.row.status === 3 && hasEmptyValues(scope.row)">跳过</el-tag> |
|||
<!-- 其他情况(包括用户修改后的状态)显示下拉框 --> |
|||
<el-select |
|||
v-else |
|||
v-model="scope.row.status" |
|||
class="set-select" |
|||
placeholder="请选择" |
|||
size="mini" |
|||
style="width: 100px; background-color: transparent;" |
|||
@change="handleStatusChange(scope.row)" |
|||
> |
|||
<el-option label="覆盖" :value="2" /> |
|||
<el-option label="跳过" :value="3" /> |
|||
</el-select> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column v-else prop="status" label="状态" align="center" width="80px"> |
|||
<template slot-scope="scope"> |
|||
<el-tag v-if="scope.row.status===1" type="success">导入</el-tag> |
|||
<el-tag v-if="scope.row.status===2" type="warning">覆盖</el-tag> |
|||
<el-tag v-if="scope.row.status===3">跳过</el-tag> |
|||
<el-tag v-if="scope.row.status===4" type="danger">失败</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column label="序号" width="55" align="center" show-overflow-tooltip> |
|||
<template slot-scope="scope"> |
|||
<span>{{ (page.page - 1) * page.size + scope.$index + 1 }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="maintitle" label="姓名" align="center" width="100" show-overflow-tooltip /> |
|||
<el-table-column prop="idType" label="身份类别" align="center" width="100" show-overflow-tooltip /> |
|||
<el-table-column prop="idNumber" label="公民身份证号" align="center" show-overflow-tooltip /> |
|||
<el-table-column prop="nativePlace" label="籍贯" align="center" show-overflow-tooltip /> |
|||
<el-table-column prop="archiveNo" label="证件号" align="center" show-overflow-tooltip /> |
|||
</el-table> |
|||
<!--分页组件--> |
|||
<el-pagination |
|||
:current-page="page.page" |
|||
:total="page.total" |
|||
:page-size="page.size" |
|||
:pager-count="5" |
|||
layout="total, prev, pager, next, sizes" |
|||
@size-change="handleSizeChange" |
|||
@current-change="handleCurrentPage" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import crudFileImport from '@/api/archivesManage/fileImport' |
|||
export default { |
|||
name: 'ListPre', // 组件名建议与文件名一致 |
|||
components: {}, |
|||
props: { |
|||
isLogOrPreview: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
hpId: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
initialTableData: { |
|||
type: Array, |
|||
default: () => [] |
|||
}, |
|||
initialTotal: { |
|||
type: Number, |
|||
default: 0 |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
tableData: [], |
|||
page: { |
|||
page: 1, |
|||
size: 10, |
|||
total: 0 |
|||
}, |
|||
selections: [], |
|||
isData: true, |
|||
// 核心:用于缓存所有被修改过的行数据 |
|||
// 结构: { 'rowId1': { ...modifiedRowData1 }, 'rowId2': { ...modifiedRowData2 } } |
|||
modifiedRowsCache: {}, |
|||
readData: null, |
|||
importData: null, |
|||
skipData: null, |
|||
coverData: null, |
|||
errorData: null |
|||
} |
|||
}, |
|||
watch: { |
|||
initialTableData(newVal) { |
|||
if (newVal && newVal.length > 0) { |
|||
this.tableData = this.applyModificationsToData(newVal) |
|||
} |
|||
}, |
|||
initialTotal(newVal) { |
|||
if (newVal !== undefined && newVal !== null) { |
|||
this.page.total = newVal |
|||
} |
|||
} |
|||
}, |
|||
created() { |
|||
if (this.initialTableData && this.initialTableData.length > 0) { |
|||
this.tableData = this.applyModificationsToData(this.initialTableData) |
|||
this.page.total = this.initialTotal |
|||
} else { |
|||
this.getList() |
|||
} |
|||
}, |
|||
methods: { |
|||
// 核心方法:将缓存中的修改应用到新的数据列表上 |
|||
applyModificationsToData(data) { |
|||
return data.map(row => { |
|||
// 假设每行都有一个唯一的id字段作为key |
|||
const cachedRow = this.modifiedRowsCache[row.id] |
|||
// 如果缓存中存在该行的修改,则返回合并后的数据(缓存中的数据优先级更高) |
|||
return cachedRow ? { ...row, ...cachedRow } : row |
|||
}) |
|||
}, |
|||
|
|||
changeDataChecked(val) { |
|||
this.page.page = 1 |
|||
this.getList() |
|||
}, |
|||
|
|||
getList() { |
|||
const pageParam = this.page.page - 1 |
|||
crudFileImport.FetchHpArchivesDetailsByHpId({ |
|||
page: pageParam, |
|||
size: this.page.size, |
|||
hpId: this.hpId, |
|||
isError: this.isLogOrPreview === 'log' ? false : this.isData |
|||
}).then(res => { |
|||
this.tableData = this.applyModificationsToData(res.data.content || []) |
|||
this.page.total = res.data.totalElements || 0 |
|||
this.doLayout() |
|||
}) |
|||
}, |
|||
|
|||
clickRowHandler(row) { |
|||
this.$refs.table.toggleRowSelection(row) |
|||
}, |
|||
|
|||
selectionChangeHandler(val) { |
|||
this.selections = val |
|||
}, |
|||
|
|||
handleSizeChange(size) { |
|||
this.page.size = size |
|||
this.page.page = 1 |
|||
this.getList() |
|||
}, |
|||
|
|||
handleCurrentPage(val) { |
|||
this.page.page = val |
|||
this.getList() |
|||
}, |
|||
|
|||
doLayout() { |
|||
this.$nextTick(() => { |
|||
if (this.$refs.table) { |
|||
this.$refs.table.doLayout() |
|||
} |
|||
}) |
|||
}, |
|||
hasEmptyValues(row) { |
|||
const fieldsToCheck = ['maintitle', 'idType', 'idNumber', 'nativePlace', 'archiveNo'] |
|||
return fieldsToCheck.some(field => { |
|||
const value = row[field] |
|||
return value === null || value === undefined || (typeof value === 'string' && value.trim() === '') |
|||
}) |
|||
}, |
|||
// 核心方法:处理状态变更 |
|||
handleStatusChange(row) { |
|||
if (!row.id) { |
|||
console.error('行数据缺少唯一的id字段,无法缓存修改!', row) |
|||
return |
|||
} |
|||
this.modifiedRowsCache[row.id] = { ...row } |
|||
this.$message.success(`证件号为 ${row.archiveNo} 姓名为 ${row.maintitle} 的状态已更新为: ${row.status === 2 ? '覆盖' : '跳过'}`) |
|||
}, |
|||
/** |
|||
* 返回一个包含所有修改行数据的数组 |
|||
*/ |
|||
getModifiedRows() { |
|||
return Object.values(this.modifiedRowsCache) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang='scss' scoped> |
|||
.import-detail-container{ |
|||
position: relative; |
|||
.import-data-number{ |
|||
position: absolute; |
|||
right: 10px; |
|||
top: 20px; |
|||
font-size: 12px; |
|||
color: #fff; |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
& p{ |
|||
margin-right: 10px; |
|||
} |
|||
& span{ |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
} |
|||
.import-tab{ |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
margin-bottom: 10px; |
|||
p{ |
|||
padding: 10px 20px 14px 20px; |
|||
font-size: 16px; |
|||
color: #339CFF; |
|||
cursor: pointer; |
|||
&.active{ |
|||
position: relative; |
|||
color: #fff; |
|||
&::after{ |
|||
content: ""; |
|||
position: absolute; |
|||
left: 0; |
|||
bottom: -1px; |
|||
width: 100%; |
|||
height: 3px; |
|||
border-radius: 3px; |
|||
background-color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.el-tag.el-tag--success { |
|||
color: #1aae93; |
|||
border-color: #1aae93; |
|||
background-color: transparent !important; |
|||
} |
|||
.set-select{ |
|||
::v-deep .el-input .el-input__inner{ |
|||
border: 1px solid #339cff; |
|||
background-color: #021941; |
|||
height: 30px; |
|||
line-height: 30px; |
|||
color: #fff; |
|||
} |
|||
} |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue