Browse Source

设备管理

master
xuhuajiao 6 months ago
parent
commit
f49b52e2d2
  1. 528
      src/views/deviceManage/area/index.vue
  2. 682
      src/views/deviceManage/bookshelf/index.vue
  3. 594
      src/views/deviceManage/bookshelfPosition/index.vue
  4. 769
      src/views/deviceManage/device/index.vue
  5. 282
      src/views/deviceManage/floor/index.vue
  6. 16
      src/views/deviceManage/index.vue
  7. 24
      src/views/faceRecognition/personInfoManage.vue
  8. 18
      src/views/faceRecognition/personRegister.vue

528
src/views/deviceManage/area/index.vue

@ -0,0 +1,528 @@
<template>
<div class="app-container">
<div class="venue-header">
<h4><i class="iconfont icon-hangzhengquyuguanli" />区域列表</h4>
<p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
</div>
<div class="venue-content">
<div class="venue-left">
<div class="head-container">
<div class="head-search">
<!-- 搜索 -->
<el-select v-model="query.floorId" clearable size="small" placeholder="楼层" class="filter-item" style="width: 80px" @change="changeInitFloorValue($event)">
<el-option v-for="item in floorOptions" :key="item.id" :label="item.floorName" :value="item.id" />
</el-select>
<el-input v-model="query.search" clearable size="small" placeholder="输入关键字搜索" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @clear="crud.toQuery" @keyup.enter.native="crud.toQuery" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<template v-slot:middle>
<el-button slot="reference" size="mini" :loading="crud.delAllLoading" :disabled="crud.selections.length === 0" @click="toDelete(crud.selections)">
<i class="iconfont icon-shanchu" />
删除
</el-button>
</template>
<template v-slot:right>
<el-button :loading="crud.downloadLoading" size="mini" :disabled="crud.selections.length <= 1" @click="showSort">
<i class="iconfont icon-paixu" />
排序
</el-button>
</template>
</crudOperation>
</div>
<div>
<!-- <div class="double-click-btn"><i class="iconfont icon-zhuyi-lan" /><span>双击列表数据查看对应书架</span></div> -->
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
style="width: 100%;"
height="506"
@selection-change="crud.selectionChangeHandler"
@row-click="clickRowHandler"
@row-dblclick="onRowDblclick"
>
<el-table-column type="selection" align="center" width="55" />
<el-table-column type="index" label="排序" />
<el-table-column prop="regionName" label="区域名称" />
<el-table-column prop="regionCode" label="区域编码" />
<el-table-column prop="floorName" label="所在楼层" />
<el-table-column prop="booksheflCount" label="书架" />
<el-table-column prop="signPoint" label="标注">
<template slot-scope="scope">
<span :class="['row-state', scope.row.signPoint ? 'end-state' : 'cancel-state' ]">{{ scope.row.signPoint ? '已标注': '未标注' }}</span>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination v-if="crud.data.length!==0" />
</div>
</div>
<div class="venue-right">
<div class="container-right tab-content">
<span class="right-top-line" />
<span class="left-bottom-line" />
<ul class="tab-nav">
<li :class="{ 'active-tab-nav': activeIndex == 0 }" @click="changeActiveTab(0)">楼层区域<i /></li>
<li :class="{ 'active-tab-nav': activeIndex == 1 }" @click="changeActiveTab(1)">区域预览<i /></li>
<!-- 最右侧装饰img -->
<span class="tab-right-img" />
<el-button size="mini" class="venue-mark" :disabled="crud.selections.length !== 1" @click="handleMark">
<i class="el-icon-edit" />
区域标注
</el-button>
</ul>
<div v-show="activeIndex == 0" class="venue-preview">
<!-- <MarkCover v-if="currentMarkData && currentMarkData.signPoint" ref="markRefs2" :is-canvas-show="true" :current-mark-data="currentMarkData" :image-floor-url="imageUrl" /> -->
<div v-show="currentMarkData && currentMarkData.signPoint ">
<canvas :id="`canvasPreview${currentMarkData && currentMarkData.id}`" :width="width" :height="height" />
</div>
<img v-if="currentMarkData && !currentMarkData.signPoint" :src="imageUrl" :onerror="defaultImg" alt="">
<img v-if="!currentMarkData" :src="imageUrl" :onerror="defaultImg" alt="">
</div>
<div v-if="activeIndex == 1" class="venue-preview">
<img :src="imageRegionUrl" :onerror="defaultImg" alt="">
</div>
</div>
</div>
</div>
<!-- form -->
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<el-form ref="form" :rules="rules" :model="form" size="small" label-width="100px">
<el-form-item label="所属楼层" prop="floorId">
<el-select v-model="form.floorId" placeholder="请选择" style="width: 225px;" @change="changeFloorValue($event)">
<el-option
v-for="(item,index) in floorOptions"
:key="index"
:label="item.floorName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="区域名称" prop="regionName">
<el-input v-model="form.regionName" style="width: 580px;" />
</el-form-item>
<el-form-item label="区域编码" prop="regionCode">
<el-input v-model="form.regionCode" style="width: 580px;" />
</el-form-item>
<el-form-item label="描述信息" prop="regionDescription">
<el-input v-model="form.regionDescription" placeholder="请输入" type="textarea" rows="3" style="width: 580px;" />
</el-form-item>
<UploadCover ref="uploadCoverRefs" :label-name="labelName" :form="form" @childCover="handleCover" />
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">保存</el-button>
</div>
</div>
</el-dialog>
<!-- 排序 -->
<el-dialog :close-on-click-modal="false" :append-to-body="true" title="排序" :visible.sync="sortVisible" @opened="opened">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<i class="drag-tip">提示请通过拖动鼠标来调整当前顺序</i>
<el-table :data="sortTableData" class="file-sort" style="width: 100%;max-height: 70vh;" row-key="id">
<el-table-column type="index" label="序号" width="100" align="center" />
<el-table-column prop="regionName" label="区域名称">
<template slot-scope="scope">
<span>{{ scope.row.regionName +' [ '+ scope.row.floorName+' ] ' }}</span>
</template>
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click.native="handleSort">保存</el-button>
</div>
</div>
</el-dialog>
<!-- 标注 -->
<el-dialog class="mark-dialog" :close-on-click-modal="false" :append-to-body="true" :title="titleMark" :visible.sync="markVisible" :before-close="handleCloseDialog">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<MarkCover ref="markRefs" :is-book-shelf="false" :current-mark-data="currentMarkData" :image-url="imageUrl" @handleCloseDialog="handleCloseDialog" />
</div>
</el-dialog>
</div>
</template>
<script>
import { FetchLibraryFloorListAll } from '@/api/floor/index'
import crudRegion from '@/api/area/index'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import rrOperation from '@crud/RR.operation'
import pagination from '@crud/Pagination'
import UploadCover from '@/views/components/upload.vue'
import MarkCover from '@/views/components/mark.vue'
import Sortable from 'sortablejs'
import { mapGetters } from 'vuex'
import defaultImg from '@/assets/images/system/default-img.jpg'
import { fabric } from 'fabric'
const defaultForm = { id: null, floorId: null, regionName: null, regionCode: null, regionDescription: null, regionMap: null, signPoint: null }
export default {
name: 'Bookshelf',
components: { crudOperation, rrOperation, pagination, UploadCover, MarkCover },
cruds() {
return CRUD({ title: '区域', url: 'api/libraryRegion/initLibraryRegionList', crudMethod: { ...crudRegion }, sort: [], optShow: {
add: true,
edit: true,
del: false,
download: false,
group: false,
reset: false
}})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
setfloorId: null,
floorOptions: [],
labelName: '区域地图',
permission: {
add: ['admin', 'floor:add'],
edit: ['admin', 'floor:edit'],
del: ['admin', 'floor:del']
},
rules: {
floorId: [
{ required: true, message: '请选择所属楼层', trigger: 'change' }
],
regionName: [
{ required: true, message: '区域名称不可为空', trigger: 'blur' }
],
regionCode: [
{ required: true, message: '区域编码不可为空', trigger: 'blur' }
]
},
activeIndex: 0,
defaultImg: defaultImg,
imageUrl: defaultImg,
imageRegionUrl: defaultImg,
sortTableData: [], // data
sortVisible: false, // dialog
markVisible: false, //
titleMark: '区域标注',
currentMarkData: null,
canvasPreview: {},
width: 900,
height: 600,
drawWidth: 2 //
}
},
computed: {
...mapGetters([
'user',
'baseApi'
])
},
watch: {
width() {
this.canvasPreview.setWidth(this.width)
},
height() {
this.canvasPreview.setHeight(this.height)
}
},
beforeDestroy() {
window.removeEventListener('beforeunload', this.clearLocalStorage)
},
methods: {
clearLocalStorage() {
const key = 'formFloor'
if (localStorage.getItem(key)) {
localStorage.removeItem(key)
}
},
[CRUD.HOOK.beforeRefresh]() {
this.getLibraryFloorListAll()
console.log(this.$route.query)
if (this.$route.query.formFloor) {
const formFloor = JSON.parse(localStorage.getItem('formFloor'))
if (formFloor) {
this.crud.query.floorId = formFloor.id
}
}
},
[CRUD.HOOK.afterRefresh](crud) {
console.log('crud.data', crud.data)
if (crud.data.length !== 0) {
this.clickRowHandler(crud.data[0])
this.activeIndex = 0
} else {
this.currentMarkData = null
this.imageUrl = this.defaultImg
this.imageRegionUrl = this.defaultImg
}
},
//
[CRUD.HOOK.afterValidateCU](crud) {
this.$refs.uploadCoverRefs.fileNames = ''
return true
},
//
getLibraryFloorListAll() {
FetchLibraryFloorListAll().then(res => {
this.floorOptions = res
}).catch(() => {
})
},
handleCover(value) {
console.log(value)
this.crud.form.regionMap = value
},
changeInitFloorValue(value) {
if (this.$route.query.formFloor) {
localStorage.removeItem('formFloor')
}
this.crud.query.floorId = value
this.crud.toQuery()
console.log('value', value)
},
changeFloorValue(value) {
},
clickRowHandler(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row)
// http://192.168.99.67:12010/api/fileRelevant/getImg?imgType=1&imgId=f6d3ecea-0456-4429-ba77-1a4921d5c806
this.currentMarkData = row
if (this.canvasPreview.lowerCanvasEl) {
this.canvasPreview.clear()
this.canvasPreview.dispose()
}
if (this.activeIndex === 0) {
if (row.floorMap) {
this.imageUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=1&imgId=' + row.floorMap
if (row.signPoint) {
this.$nextTick(() => {
const drawinfo = JSON.parse(row.signPoint)
this.initCanvasPreview(drawinfo)
})
}
} else {
this.imageUrl = this.defaultImg
}
} else {
if (row.regionMap) {
this.imageRegionUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=1&imgId=' + row.regionMap
} else {
this.imageRegionUrl = this.defaultImg
}
}
},
onRowDblclick(row) {
localStorage.setItem('formArea', JSON.stringify(row))
// this.$router.push({ path: '/check/venueDevice/bookshelf', query: { 'formArea': true }})
},
changeActiveTab(data) {
this.activeIndex = data
if (this.crud.selections.length === 1) {
if (this.crud.selections[0].floorMap) {
this.currentMarkData = this.crud.selections[0]
this.imageUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=1&imgId=' + this.crud.selections[0].floorMap
} else {
this.imageUrl = this.defaultImg
}
if (this.crud.selections[0].regionMap) {
this.imageRegionUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=1&imgId=' + this.crud.selections[0].regionMap
} else {
this.imageRegionUrl = this.defaultImg
}
if (this.activeIndex === 0) {
if (this.crud.selections[0].signPoint) {
console.log('1111')
this.$nextTick(() => {
const drawinfo = JSON.parse(this.crud.selections[0].signPoint)
this.initCanvasPreview(drawinfo)
})
}
}
}
},
async handleMark() {
if (this.crud.selections[0].floorMap) {
this.markVisible = true
this.currentMarkData = this.crud.selections[0]
this.titleMark = this.currentMarkData.regionName + ' - 区域标注'
this.$nextTick(() => {
this.$refs.markRefs.drawinfo = this.currentMarkData && this.currentMarkData.signPoint ? JSON.parse(this.currentMarkData.signPoint) : null
this.$refs.markRefs.initCanvas()
})
} else {
this.$message({ message: '请先上传当前楼层图', type: 'error', offset: 8 })
}
},
toDelete(datas) {
this.$confirm('此操作将删除当前所选区域<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
this.crud.delAllLoading = true
const ids = []
datas.forEach(val => {
ids.push(val.id)
})
console.log(ids)
crudRegion.del(ids).then(res => {
console.log(res)
this.$message({ message: res, type: 'success', offset: 8 })
this.crud.delAllLoading = false
this.crud.refresh()
}).catch(err => {
this.crud.delAllLoading = false
console.log(err)
})
}).catch(() => {
this.crud.delAllLoading = false
})
},
// -
rowDrop(className, targetName) {
//
const tbody = document.querySelector('.' + className + ' .el-table__body-wrapper tbody')
const that = this
Sortable.create(tbody, {
//
draggable: '.el-table__row',
onEnd({ newIndex, oldIndex }) {
if (newIndex === oldIndex) return
that[targetName].splice(newIndex, 0, that[targetName].splice(oldIndex, 1)[0])
}
})
},
//
opened() {
this.rowDrop('file-sort', 'sortTableData')
},
showSort() {
this.sortVisible = true
this.sortTableData = JSON.parse(JSON.stringify(this.crud.selections))
},
handleSort() {
const data = this.sortTableData.map((value, index) => {
return { id: value.id, isSequence: index + 1 }
})
this.sortTableData.forEach((item, index) => {
item.isSequence = index + 1
})
crudRegion.sort(data).then(() => {
this.sortVisible = false
this.$message({ message: '保存成功', type: 'success', offset: 8 })
this.crud.refresh()
})
},
handleCloseDialog() {
// if (this.$refs.markRefs.canvas) {
// this.$refs.markRefs.canvas.clear()
// this.$refs.markRefs.canvas.dispose()
// }
this.markVisible = false
this.crud.refresh()
},
initCanvasPreview(drawinfo) {
if (!this.currentMarkData) {
console.error('currentMarkData is null or undefined')
return
}
const canvasId = `canvasPreview${this.currentMarkData.id}`
this.canvasPreview = new fabric.Canvas(canvasId, {
skipTargetFind: false,
selectable: false,
selection: false
})
this.$nextTick(() => {
this.canvasPreview.selectionColor = 'rgba(0,0,0,0.05)'
this.loadDrawPreview(drawinfo)
this.canvasPreview.on('mouse:wheel', this.mouse)
})
},
//
mouse(e) {
if (undefined === e) return
let zoom = (e.e.deltaY > 0 ? -0.1 : 0.1) + this.canvasPreview.getZoom()
zoom = Math.max(0.8, zoom)
// 1/10
zoom = Math.min(3, zoom)
// 3
const zoomPoint = new fabric.Point(e.e.pageX, e.e.pageY)
this.canvasPreview.zoomToPoint(zoomPoint, zoom)
},
//
loadDrawPreview(drawinfo) {
const self = this
const pointGroup = drawinfo.pointInfo
const imgInfo = drawinfo.imgInfo
imgInfo.src = self.imageUrl
//
fabric.util.enlivenObjects([imgInfo], objects => {
objects.forEach(o => {
o.selectable = false
o.hasControls = false
o.centeredScaling = false
self.canvasPreview.add(o)
})
//
pointGroup.forEach(async(item, index) => {
if (item.pointInfo !== '') {
const polygon = new fabric.Polygon(item.pointInfo, {
name: item.name,
stroke: 'rgba(196,43, 1, 1)',
strokeWidth: self.drawWidth,
fill: 'rgba(196,43, 1, 1)',
opacity: 0.5,
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'left', //
originY: 'top' //
})
// polygon.index = index
self.canvasPreview.add(polygon)
polygon.on('mousedown', function(e) {
console.log('Rect ' + (index + 1) + ' clicked', e)
console.log('e.target.name', e.target.name)
})
polygon.on('mouseover', function(e) {
console.log('e', e)
console.log('e.target', e.target)
console.log('e.target.name', e.target.name)
this.set({ opacity: 0.8, hoverCursor: 'pointer' })
self.canvasPreview.renderAll()
})
//
polygon.on('mouseout', function() {
this.set({ opacity: 0.5 })
self.canvasPreview.renderAll()
})
}
})
})
self.canvasPreview.renderAll()
}
}
}
</script>
<style lang="scss" scoped>
.tab-content{
min-height: calc(100vh - 232px) !important;
}
</style>

682
src/views/deviceManage/bookshelf/index.vue

@ -0,0 +1,682 @@
<template>
<div class="app-container">
<div class="venue-header">
<h4><i class="iconfont icon-shujia" />书架列表</h4>
<p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
</div>
<div class="venue-content">
<div class="venue-left">
<div class="head-container">
<div class="head-search">
<!-- 搜索 -->
<el-select v-model="selectFloorVal" size="small" placeholder="楼层" class="filter-item" style="width: 80px" value-key="id" @change="changeBeforeFloor">
<el-option v-for="(item,index) in floorOptions" :key="index" :label="item.floorName" :value="item" />
</el-select>
<el-select v-model="selectRegionVal" size="small" placeholder="区域" class="filter-item" style="width: 140px" value-key="id" @change="changeBeforeRegion">
<el-option v-for="(item,index) in regionOptions" :key="index" :label="item.regionName" :value="item" />
</el-select>
<!-- <el-input v-model="query.search" clearable size="small" placeholder="输入关键字搜索" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @clear="crud.toQuery" @keyup.enter.native="crud.toQuery" /> -->
<!-- <rrOperation /> -->
</div>
<crudOperation :permission="permission">
<template v-slot:middle>
<el-button slot="reference" size="mini" :loading="crud.delAllLoading" :disabled="crud.selections.length === 0" @click="toDelete(crud.selections)">
<i class="iconfont icon-shanchu" />
删除
</el-button>
</template>
</crudOperation>
</div>
<div>
<div class="double-click-btn"><i class="iconfont icon-zhuyi-lan" /><span>双击列表数据查看对应架位</span></div>
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
style="width: 100%;"
height="507"
@selection-change="selectionChangeHandler"
@row-dblclick="onRowDblclick"
@row-click="clickRowHandler"
>
<el-table-column type="selection" align="center" width="55" />
<!-- <el-table-column type="index" label="排序" /> -->
<el-table-column prop="shelfName" label="书架名称" />
<el-table-column prop="shelfShelf" label="书架规格">
<template slot-scope="scope">
<span>{{ scope.row.shelfFloor + ' X ' + scope.row.shelfShelf }}</span>
</template>
</el-table-column>
<el-table-column prop="rowType" label="单/双面">
<template slot-scope="scope">
<span>{{ scope.row.rowType === 1 ? '单面' :'双面' }}</span>
</template>
</el-table-column>
<el-table-column prop="floorName" label="所属楼层" />
<el-table-column prop="regionName" label="所属区域" min-width="100" />
<el-table-column prop="signPoint" label="标注">
<template slot-scope="scope">
<span :class="['row-state', scope.row.signPoint ? 'end-state' : 'cancel-state' ]">{{ scope.row.signPoint ? '已标注': '未标注' }}</span>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination v-if="crud.data.length!==0" />
</div>
</div>
<div class="venue-right">
<div class="container-right tab-content">
<span class="right-top-line" />
<span class="left-bottom-line" />
<ul class="tab-nav">
<li class="active-tab-nav">区域书架<i /></li>
<!-- 最右侧装饰img -->
<span class="tab-right-img" />
<el-button size="mini" class="venue-mark" :disabled="crud.selections.length !== 1" @click="handleMark">
<i class="el-icon-edit" />
书架标注
</el-button>
</ul>
<div class="venue-preview">
<!-- <img :src="imageUrl" :onerror="defaultImg" alt=""> -->
<div v-show="currentMarkData && currentMarkData.signPoint">
<canvas id="canvasPreview" :width="width" :height="height" />
</div>
<img v-if="currentMarkData && !currentMarkData.signPoint" :src="imageUrl" :onerror="defaultImg" alt="">
<img v-if="!currentMarkData" :src="imageUrl" :onerror="defaultImg" alt="">
</div>
</div>
</div>
</div>
<!-- form -->
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<el-form ref="form" :rules="rules" :model="form" inline size="small" label-width="90px">
<el-form-item label="所属楼层" prop="floorName">
<el-input v-model="form.floorName" disabled />
</el-form-item>
<el-form-item label="所属区域" prop="regionName">
<el-input v-model="form.regionName" disabled />
</el-form-item>
<el-row>
<el-form-item label="单/双排" prop="rowType">
<el-radio-group v-model="form.rowType" v-removeAriaHidden size="mini">
<el-radio :label="1">单排</el-radio>
<el-radio :label="2">双排</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.rowType===1" label="A/B面" prop="toward">
<el-select v-model="form.toward" placeholder="请选择" style="width: 225px;">
<el-option
v-for="(item,index) in abOptions"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-row>
<el-row>
<el-form-item label="书架排号" prop="shelfRow">
<el-input v-model="form.shelfRow" @blur="formatShelfRow" />
</el-form-item>
<el-form-item label="书架名称" prop="shelfName">
<el-input v-model="computedShelfName" disabled />
</el-form-item>
</el-row>
<el-form-item label="书架规格" prop="rackSpecs">
<el-input-number v-model.number="form.shelfFloor" :min="1" :max="999" controls-position="right" style="width:80px;" />
<span style="font-size: 12px;"></span>
<span style="padding:0 2px;"></span>
<el-input-number v-model.number="form.shelfShelf" :min="1" :max="999" controls-position="right" style="width: 80px;" />
<span style="font-size: 12px;"></span>
</el-form-item>
<el-form-item label="架起始标" prop="startShelf">
<el-input-number v-model.number="form.startShelf" :min="1" :max="999" controls-position="right" />
</el-form-item>
<el-row>
<el-form-item label="架号顺序" prop="shelfType">
<el-select v-model="form.shelfType" placeholder="请选择" style="width: 586px;">
<el-option
v-for="(item,index) in rackOrderOptions"
:key="index"
:label="item.name"
:value="item.key"
/>
</el-select>
</el-form-item>
</el-row>
<el-row>
<el-form-item label="层号顺序" prop="floorType">
<el-select v-model="form.floorType" placeholder="请选择" style="width: 586px;">
<el-option
v-for="(item,index) in layerSeqOptions"
:key="index"
:label="item.name"
:value="item.key"
/>
</el-select>
</el-form-item>
</el-row>
<el-form-item label="倒架规则" prop="shelfRule">
<el-radio-group v-model="form.shelfRule" v-removeAriaHidden size="mini">
<el-radio :label="1">有序</el-radio>
<el-radio :label="2">无序</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="错架判断" prop="shelfErrorJudge">
<el-radio-group v-model="form.shelfErrorJudge" v-removeAriaHidden size="mini">
<el-radio :label="2">格子</el-radio>
<el-radio :label="1">书架</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">保存</el-button>
</div>
</div>
</el-dialog>
<!-- 标注 -->
<el-dialog class="mark-dialog" :close-on-click-modal="false" :append-to-body="true" :title="titleMark" :visible.sync="markVisible" :before-close="handleCloseDialog">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<MarkCover ref="markRefs" :is-book-shelf="true" :current-mark-data="currentMarkData" :image-url="imageUrl" @handleCloseDialog="handleCloseDialog" />
</div>
</el-dialog>
</div>
</template>
<script>
import { FetchLibraryFloorListAll } from '@/api/floor/index'
import { FetchInitLibraryRegionList } from '@/api/area/index'
import crudShelf from '@/api/shelf/index'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import { mapGetters } from 'vuex'
import defaultImg from '@/assets/images/system/default-img.jpg'
import MarkCover from '@/views/components/mark.vue'
import { fabric } from 'fabric'
const defaultForm = { id: null, floorName: null, floorId: null, regionName: null, rowType: 1, toward: 1, shelfRow: '', shelfName: '', shelfShelf: null, shelfFloor: null, startShelf: null, shelfType: 1, floorType: 1, shelfRule: 1, shelfErrorJudge: 2, signPoint: '' }
export default {
name: 'Bookshelf',
components: { crudOperation, pagination, MarkCover },
cruds() {
return CRUD({ title: '书架', idField: 'shelfId', url: 'api/bookShelf/initBookShelfList', crudMethod: { ...crudShelf }, sort: [], optShow: {
add: true,
edit: true,
del: false,
download: false,
group: false,
reset: false
},
queryOnPresenterCreated: false
})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
permission: {
add: ['admin', 'floor:add'],
edit: ['admin', 'floor:edit'],
del: ['admin', 'floor:del']
},
floorOptions: [],
regionOptions: [],
selectFloorVal: null,
selectRegionVal: null,
abOptions: [
{ value: 1, name: 'A面' },
{ value: 2, name: 'B面' }
],
rackOrderOptions: [
{ key: 1, name: '始终最左边为第1架(S型排架)' },
{ key: 2, name: 'A面最左为第1架(B面最左为最后1架)' },
{ key: 3, name: 'B面最左为第1架(A面最左为最后1架)' }
],
layerSeqOptions: [
{ key: 1, name: '最顶层为第一层(从上至下)' },
{ key: 2, name: '最底层为第一层(从下至上)' }
],
rules: {
floorName: [
{ required: true, message: '所属楼层不可为空', trigger: 'blur' }
],
regionName: [
{ required: true, message: '所属区域不可为空', trigger: 'blur' }
],
rowType: [
{ required: true, message: '请选择单双排', trigger: 'change' }
],
shelfRow: [
{ required: true, message: '书架排号不可为空', trigger: 'blur' }
],
shelfName: [
{ validator: this.validateShelfName, trigger: 'blur' }
],
rackSpecs: [
{ validator: this.validateRackSpecs, trigger: 'blur' }
],
startShelf: [
{ required: true, message: '架起始标不可为空', trigger: 'blur' }
],
shelfType: [
{ required: true, message: '请选择架号顺序', trigger: 'change' }
],
floorType: [
{ required: true, message: '请选择层号顺序', trigger: 'change' }
],
shelfRule: [
{ required: true, message: '请选择倒架规则', trigger: 'change' }
],
shelfErrorJudge: [
{ required: true, message: '请选择错架判断', trigger: 'change' }
]
},
defaultImg: defaultImg,
imageUrl: defaultImg,
markVisible: false, //
titleMark: '书架标注',
currentMarkData: null,
canvasPreview: {},
width: 900,
height: 600,
drawWidth: 2 //
}
},
computed: {
...mapGetters([
'user',
'baseApi'
]),
computedShelfName() {
const { shelfRow, rowType, toward } = this.form
if (!shelfRow) {
return ''
}
const baseName = `${shelfRow}`
if (rowType === 1 && toward !== null) {
return `${baseName}${this.abOptions.find(option => option.value === toward).name}`
} else if (rowType === 2) {
return baseName
}
return ''
}
},
watch: {
computedShelfName(newVal) {
this.form.shelfName = newVal
},
width() {
this.canvasPreview.setWidth(this.width)
},
height() {
this.canvasPreview.setHeight(this.height)
}
},
beforeDestroy() {
window.removeEventListener('beforeunload', this.clearLocalStorage)
},
created() {
this.getLibraryFloorListAll()
},
mounted() {
},
methods: {
clearLocalStorage() {
const key = 'formArea'
if (localStorage.getItem(key)) {
localStorage.removeItem(key)
}
},
formatShelfRow() {
let value = this.form.shelfRow
value = value.toString()
if (value.length < 3) {
value = value.padStart(3, '0')
} else if (value.length > 3) {
value = value.slice(0, 3)
}
this.form.shelfRow = value
},
validateShelfName(rule, value, callback) {
if (this.form.shelfRow) {
if (!value) {
callback(new Error('书架名称不能为空'))
} else {
callback()
}
} else {
callback()
}
},
validateRackSpecs(rule, value, callback) {
// shelfShelf shelfFloor
if (!this.form.shelfShelf) {
callback(new Error('请输入书架规格中书架架数'))
} else if (!this.form.shelfFloor) {
callback(new Error('请输入书架规格中书架层数'))
} else {
callback()
}
},
[CRUD.HOOK.beforeRefresh]() {
console.log(this.$route.query)
if (this.$route.query.formArea) {
const formArea = JSON.parse(localStorage.getItem('formArea'))
if (formArea) {
this.selectFloorVal = {
id: formArea.floorId,
floorMap: formArea.floorMap,
floorName: formArea.floorName
}
this.selectRegionVal = formArea
this.crud.query.floorId = this.selectFloorVal.id
this.crud.query.regionId = this.selectRegionVal.id
FetchInitLibraryRegionList({ 'floorId': this.selectFloorVal.id }).then(res => {
this.regionOptions = res.content
}).catch(() => {
})
}
}
},
[CRUD.HOOK.afterRefresh](crud) {
console.log(crud.data)
if (this.selectRegionVal && this.selectRegionVal.regionMap) {
this.imageUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=1&imgId=' + this.selectRegionVal.regionMap
} else {
this.imageUrl = defaultImg
}
},
//
[CRUD.HOOK.afterToCU](crud, form) {
console.log(form)
console.log('crud.query.floorId', crud.query.floorId)
console.log('selectFloorVal', this.selectFloorVal)
console.log('crud.query.regionId', crud.query.regionId)
console.log('selectRegionVal', this.selectRegionVal)
form.floorId = this.selectFloorVal.id
form.floorName = this.selectFloorVal.floorName
form.regionId = this.selectRegionVal.id
form.regionName = this.selectRegionVal.regionName
},
//
[CRUD.HOOK.beforeToEdit](crud, form) {
const params = {
'shelfId': this.crud.selections[0].shelfId
}
crudShelf.FetchBookShelfDetails(params).then(res => {
form.id = res.id
// / 1 2
form.rowType = res.rowType
// A/B 1 A 2 B
form.toward = res.toward
//
form.shelfRow = res.shelfRow
//
form.shelfName = res.shelfName
//
form.shelfShelf = res.shelfShelf
//
form.shelfFloor = res.shelfFloor
//
form.startShelf = res.startShelf
//
form.shelfType = res.shelfType
//
form.floorType = res.floorType
// 1 2
form.shelfRule = res.shelfRule
// 1 2
form.shelfErrorJudge = res.shelfErrorJudge
}).catch(() => {
})
},
//
[CRUD.HOOK.afterValidateCU](crud) {
console.log(crud.form)
delete crud.form.floorName
delete crud.form.regionName
return true
},
//
getLibraryFloorListAll() {
FetchLibraryFloorListAll().then(res => {
this.floorOptions = res
if (this.floorOptions.length > 0) {
this.selectFloorVal = this.floorOptions[0]
this.crud.query.floorId = this.selectFloorVal.id
if (this.crud.query.floorId) {
this.getInitLibraryRegionList(this.crud.query.floorId)
}
}
}).catch(() => {
})
},
getInitLibraryRegionList(val) {
const params = {
'floorId': val
}
FetchInitLibraryRegionList(params).then(res => {
this.regionOptions = res.content
if (this.regionOptions.length > 0) {
this.selectRegionVal = this.regionOptions[0]
this.crud.query.regionId = this.selectRegionVal.id
} else {
this.selectRegionVal = null
this.crud.query.regionId = null
}
this.crud.toQuery()
}).catch(() => {
})
},
changeBeforeFloor(val) {
if (this.$route.query.formArea) {
localStorage.removeItem('formArea')
}
if (val) {
this.selectFloorVal = val
this.crud.query.floorId = val.id
this.getInitLibraryRegionList(val.id)
}
},
changeBeforeRegion(val) {
if (this.$route.query.formArea) {
localStorage.removeItem('formArea')
}
if (val) {
this.selectRegionVal = val
this.crud.query.regionId = val.id
this.crud.toQuery()
}
},
selectionChangeHandler(val) {
this.crud.selections = val
// 1
if (this.crud.selections.length === 1 && this.crud.selections[0].signPoint) {
this.currentMarkData = val[0]
if (this.canvasPreview.lowerCanvasEl) {
this.canvasPreview.clear()
this.canvasPreview.dispose()
}
try {
const drawinfo = JSON.parse(this.crud.selections[0].signPoint)
this.initCanvasPreview(drawinfo)
} catch (error) {
console.error(error)
this.resetImageUrl()
}
} else {
// 10
this.currentMarkData = null
this.setImageUrlBasedOnRegionMap()
}
},
setImageUrlBasedOnRegionMap() {
if (this.selectRegionVal && this.selectRegionVal.regionMap) {
this.imageUrl = `${this.baseApi}/api/fileRelevant/getImg?imgType=1&imgId=${this.selectRegionVal.regionMap}`
} else {
this.resetImageUrl()
}
},
resetImageUrl() {
this.imageUrl = this.defaultImg
},
async handleMark() {
if (this.crud.selections.length === 1) {
const selection = this.crud.selections[0]
if (selection && this.selectRegionVal.regionMap) {
this.markVisible = true
this.currentMarkData = selection
this.titleMark = this.currentMarkData.shelfName + ' - 书架标注'
this.$nextTick(() => {
this.$refs.markRefs.drawinfo = this.currentMarkData && this.currentMarkData.signPoint ? JSON.parse(this.currentMarkData.signPoint) : null
this.$refs.markRefs.initCanvas()
})
} else {
this.$message({ message: '请先上传当前区域图', type: 'error', offset: 8 })
}
} else {
console.error('666')
}
},
clickRowHandler(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row)
},
onRowDblclick(row) {
crudShelf.FetchBookShelfDetails({ 'shelfId': row.shelfId }).then(res => {
this.$router.push({ path: '/bookshelf/bookshelfPosition', query: { 'floorName': row.floorName, 'regionName': row.regionName }})
localStorage.setItem('bookShelfDetails', JSON.stringify(res))
}).catch(() => {
})
},
toDelete(datas) {
this.$confirm('此操作将删除当前所选书架<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
this.crud.delAllLoading = true
const ids = []
datas.forEach(val => {
ids.push(val.shelfId)
})
console.log(ids)
crudShelf.del(ids).then(res => {
console.log(res)
this.$message({ message: res, type: 'success', offset: 8 })
this.crud.delAllLoading = false
this.crud.refresh()
}).catch(err => {
this.crud.delAllLoading = false
console.log(err)
})
}).catch(() => {
this.crud.delAllLoading = false
})
},
handleCloseDialog() {
this.markVisible = false
this.crud.refresh()
},
initCanvasPreview(drawinfo) {
this.canvasPreview = new fabric.Canvas('canvasPreview', {
skipTargetFind: false,
selectable: false,
selection: false
})
this.canvasPreview.selectionColor = 'rgba(0,0,0,0.05)'
this.loadDrawPreview(drawinfo)
this.canvasPreview.on('mouse:wheel', this.mouse)
},
//
mouse(e) {
if (undefined === e) return
let zoom = (e.e.deltaY > 0 ? -0.1 : 0.1) + this.canvasPreview.getZoom()
zoom = Math.max(0.8, zoom)
// 1/10
zoom = Math.min(3, zoom)
// 3
const zoomPoint = new fabric.Point(e.e.pageX, e.e.pageY)
this.canvasPreview.zoomToPoint(zoomPoint, zoom)
},
//
loadDrawPreview(drawinfo) {
const self = this
const pointGroup = drawinfo.pointInfo
const imgInfo = drawinfo.imgInfo
imgInfo.src = self.imageUrl
//
fabric.util.enlivenObjects([imgInfo], objects => {
objects.forEach(o => {
o.selectable = false
o.hasControls = false
o.centeredScaling = false
self.canvasPreview.add(o)
})
//
pointGroup.forEach(async(item, index) => {
if (item.pointInfo !== '') {
const polygon = new fabric.Polygon(item.pointInfo, {
name: item.name,
stroke: 'rgba(196,43, 1, 1)',
strokeWidth: self.drawWidth,
fill: 'rgba(196,43, 1, 1)',
opacity: 0.5,
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'left', //
originY: 'top' //
})
// polygon.index = index
self.canvasPreview.add(polygon)
polygon.on('mousedown', function(e) {
console.log('Rect ' + (index + 1) + ' clicked', e)
console.log('e.target.name', e.target.name)
})
polygon.on('mouseover', function(e) {
console.log('e', e)
console.log('e.target', e.target)
console.log('e.target.name', e.target.name)
this.set({ opacity: 0.8, hoverCursor: 'pointer' })
self.canvasPreview.renderAll()
})
//
polygon.on('mouseout', function() {
this.set({ opacity: 0.5 })
self.canvasPreview.renderAll()
})
}
})
})
self.canvasPreview.renderAll()
}
}
}
</script>
<style lang="scss" scoped>
.tab-content{
min-height: calc(100vh - 232px) !important;
}
</style>

594
src/views/deviceManage/bookshelfPosition/index.vue

@ -0,0 +1,594 @@
<template>
<div class="app-container">
<div class="venue-header">
<h4><i class="iconfont icon-shujia" />架位列表</h4>
<span class="bookshelf-area">{{ floorName }} - {{ regionName }}</span>
<p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
</div>
<div class="bookshelf-main">
<div class="bookshelf-top">
<ul class="bookshelf-info">
<li><p>书架名称</p><span>{{ bookShelfDetails && bookShelfDetails.shelfName }}</span></li>
<li><p>书架规格</p><span>{{ bookShelfDetails && bookShelfDetails.shelfFloor + ' X ' + bookShelfDetails.shelfShelf }}</span></li>
<li><p>/双面</p><span>{{ bookShelfDetails && bookShelfDetails.rowType === 1 ? '单面' :'双面' }}</span></li>
<li><p>倒架规则</p><span>{{ bookShelfDetails && bookShelfDetails.shelfRule === 1 ? '无序' :'有序' }}</span></li>
<li><p>错架判断</p><span>{{ bookShelfDetails && bookShelfDetails.shelfErrorJudge === 1 ? '书架' :'格子' }}</span></li>
<!-- <li><p>前端测试用-架号顺序</p><span>{{ bookShelfDetails && bookShelfDetails.shelfType === 1 ? '始终最左边为第1架(S型排架)' : (bookShelfDetails.shelfType === 2 ? 'A面最左为第1架(B面最左为最后1架)' : 'B面最左为第1架(A面最左为最后1架)') }}</span></li>
<li><p>前端测试用-层号顺序</p><span>{{ bookShelfDetails && bookShelfDetails.floorType === 1 ? '最顶层为第一层(从上至下)' :'最底层为第一层(从下至上)' }}</span></li> -->
</ul>
<div class="bookshelf-button">
<el-button size="mini" @click="doExport">
<i class="iconfont icon-daochu" />
导出层位编码
</el-button>
<el-button size="mini" :disabled="!cellInfo.cameraId" @click="handleViewVideo(cellInfo.cameraId)">
<i class="iconfont icon-yulan" />
{{ cellInfo.cameraId ? '摄像头预览' : '未绑定摄像头' }}
</el-button>
</div>
</div>
<div class="bookshelf-layer-info">
<div class="bookshelf-left">
<swiper
ref="swiperTitle"
class="swiper-title"
:options="swiperOptionTitle"
:auto-update="true"
:auto-destroy="true"
:delete-instance-on-destroy="true"
:cleanup-styles-on-destroy="true"
>
<swiper-slide
v-for="(item, index) of tabListData"
ref="swiperSlideItem"
:key="'name' + index"
:iname="item.name"
class="swiper-slide-title"
>
<div
class="tab-name"
:class="{ active: index === swiperActiveIndex }"
@click="handleSlidClickFun(index)"
>
{{ item.name }}
</div>
</swiper-slide>
</swiper>
<swiper
ref="swiperContent"
class="swiper-content"
:style="rowStyle"
:options="swiperOptionContent"
:auto-update="true"
:auto-destroy="true"
:delete-instance-on-destroy="true"
:cleanup-styles-on-destroy="true"
>
<swiper-slide
v-for="(item, index) of tabListData"
:key="'content' + index"
class="swiper-slide-content"
>
<ul class="cabinet-row" :style="rowStyle">
<li v-for="(shelf,i) in booShelfGrid" :key="i" class="cabinet-cell" :style="cellStyle" :class="{ active: i === cellIndex }" @click="handleCellCurrent(shelf,i)">
<span>{{ shelf.gridName | removeQUPrefix }}</span>
</li>
</ul>
</swiper-slide>
</swiper>
</div>
<div class="bookshelf-right-info">
<div class="layer-status">
<span v-if="cellInfo.startSortmark && cellInfo.endSortmark && checkValue === 'true'" class="row-state end-state">正常盘点</span>
<span v-if="!cellInfo.startSortmark && !cellInfo.endSortmark && checkValue === 'true'" class="row-state soon-state">待初始化</span>
<span v-if="checkValue === 'false'" class="row-state cancel-state">停止盘点</span>
<span v-if="bookSortValue === 'false'" class="row-state other-state">无序倒架</span>
<span v-else class="row-state ing-state">有序倒架</span>
</div>
<h5 class="layer-name">{{ cellInfo.gridName | removeQUPrefix }}</h5>
<div class="layer-code-sort">
<ul>
<!-- I247.58/586 -->
<li><p>起始索书号</p><span>{{ cellInfo.startSortmark ? cellInfo.startSortmark : '-' }}</span></li>
<li><p>结束索书号</p><span>{{ cellInfo.endSortmark? cellInfo.endSortmark : '-' }}</span></li>
</ul>
<el-button size="mini" class="edit-callNumber" @click="handleEditGridNum">
<!-- <i class="iconfont icon-yulan" /> -->
<!-- <i>&nbsp;&nbsp;&nbsp;</i>
<i>索书号</i> -->
编辑索书号
</el-button>
</div>
<ul class="layer-handle">
<li>
<p>层位盘点开关</p>
<el-switch
v-model="checkValue"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="true"
inactive-value="false"
@change="changeCheckSwitch"
/>
</li>
<li>
<p>图书有序检查</p>
<el-switch
v-model="bookSortValue"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="true"
inactive-value="false"
@change="changeBookSortSwitch"
/>
</li>
</ul>
</div>
</div>
</div>
<!-- 编辑索书号 -->
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :visible="callNumVisible" title="编辑索书号范围" :before-close="handleClose">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<el-form ref="form" :rules="rules" :model="form" size="small" label-width="100px">
<el-form-item label="所属架位" prop="gridName">
<el-input v-model="form.gridName" disabled style="width: 580px;" />
</el-form-item>
<el-form-item label="起始索书号" prop="startSortmark">
<el-input v-model="form.startSortmark" style="width: 580px;" />
</el-form-item>
<el-form-item label="结束索书号" prop="endSortmark">
<el-input v-model="form.endSortmark" style="width: 580px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click.native="handleClose">取消</el-button>
<el-button type="primary" @click.native="handleSaveCallNum">保存</el-button>
</div>
</div>
</el-dialog>
<!-- 查看监控视频 -->
<el-dialog class="view-video" append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :visible="hkVideoVisible" title="查看监控视频" :before-close="handleClose">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<hkVideo ref="hkVideoRef" :hk-config="hkConfig" />
</div>
</el-dialog>
</div>
</template>
<script>
// https://blog.csdn.net/qq_37236395/article/details/119737898
import { FetchInitShelfGridByShelfId, FetcheEditSortmarkByGrid, FetchChangeOrderByGrid, FetchChangeCheckByGrid } from '@/api/shelf/index'
import { FetchDeviceById } from '@/api/deviceVI/index'
import { mapGetters } from 'vuex'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
import { parseTime, saveAs, getBlob } from '@/utils/index'
import qs from 'qs'
import hkVideo from '@/views/components/hkVideo.vue'
export default {
name: 'BookshelfPosition',
components: { swiper, swiperSlide, hkVideo },
data() {
const _this = this
return {
floorName: null,
regionName: null,
bookShelfDetails: null,
booShelfGrid: null,
cellInfo: {
gridName: null,
startSortmark: null,
endSortmark: null,
cameraId: null
},
callNumVisible: false,
layerNum: 0,
rackNum: 0,
checkValue: 'true',
bookSortValue: 'true',
swiperActiveIndex: 0,
cellIndex: null,
swiperOptionContent: {
slidesPerView: 'auto',
on: {
slideChangeTransitionStart: function() {
_this.cellIndex = null
_this.swiperActiveIndex = this.activeIndex
console.log('activeIndexffff', this.swiperActiveIndex)
_this.swiperTitle.slideTo(this.activeIndex, 500, false)
}
}
},
swiperOptionTitle: {
slidesPerView: 'auto',
freeMode: true
},
tabListData: [],
form: {
id: null,
gridName: null,
startSortmark: null,
endSortmark: null,
check: null,
order: null,
cameraId: null
},
rules: {
gridName: [
{ required: true, message: '所属架位不可为空', trigger: 'blur' }
],
startSortmark: [
{ required: true, message: '起始索书号不可为空', trigger: 'blur' }
],
endSortmark: [
{ required: true, message: '结束索书号不可为空', trigger: 'blur' }
]
},
hkVideoVisible: false,
hkConfig: {
'username': null,
'password': null,
'ip': null,
'port': null
}
}
},
computed: {
...mapGetters([
'user',
'baseApi'
]),
swiperContent() {
return this.$refs.swiperContent.$el.swiper
},
swiperTitle() {
return this.$refs.swiperTitle.$el.swiper
},
cellStyle: function() {
// const h = '100%/' + this.layerNum
// const w = '100%/' + this.rackNum
const h = '60px'
const w = '146px'
return { width: `calc(${w} - 4px )`, height: `calc(${h} - 2px)` }
},
rowStyle: function() {
const w = 146 * this.rackNum + 'px'
return { width: `calc(${w})` }
}
},
watch: {
'$route'(val, from) { //
if (this.$route.query) {
this.floorName = this.$route.query.floorName
this.regionName = this.$route.query.regionName
}
}
},
async created() {
if (this.$route.query) {
this.floorName = this.$route.query.floorName
this.regionName = this.$route.query.regionName
}
if (localStorage.getItem('bookShelfDetails')) {
this.bookShelfDetails = JSON.parse(localStorage.getItem('bookShelfDetails'))
this.layerNum = this.bookShelfDetails.shelfFloor
this.rackNum = this.bookShelfDetails.shelfShelf
// /
this.tabListData = this.bookShelfDetails.rowType === 1
? this.bookShelfDetails.toward === 1
? [{ name: 'A面' }]
: [{ name: 'B面' }]
: [{ name: 'A面' }, { name: 'B面' }]
//
this.getInitShelfGridByShelfId(this.bookShelfDetails.toward)
}
},
mounted() {
},
methods: {
removeAreaPrefix(gridNames) {
const index = gridNames.indexOf('区')
if (index !== -1) {
return gridNames.substring(index + 1)
}
return gridNames
},
getInitShelfGridByShelfId(toward) {
// rowType 1 2
// toward 1 A 2 B
// shelfType 1 '1S'
// shelfType 2 'A1B1'
// shelfType 3 'B1A1'
// floorType 1 ''
// floorType 2 ''
FetchInitShelfGridByShelfId({ 'shelfId': this.bookShelfDetails.id, 'toward': toward }).then(res => {
const sortFunction = toward === 1 ? {
1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
2: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
3: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' }
} : {
1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
2: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' },
3: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' }
}
const shelfType = this.bookShelfDetails.shelfType
const floorType = this.bookShelfDetails.floorType
const sortMethod = sortFunction[shelfType][floorType]
this.booShelfGrid = this[sortMethod](res)
}).catch(() => {
})
},
// ,
sortBookshelvesLeftTop(data) {
const sortedData = []
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
for (let i = 1; i <= maxFloor; i++) {
for (let j = 1; j <= maxShelf; j++) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
// ,,
sortBookshelvesRightTop(data) {
const sortedData = []
//
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0])))
for (let i = 1; i <= maxFloor; i++) {
//
for (let j = maxShelf; j >= 1; j--) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
// ,
sortBookshelvesLeftBottom(data) {
const sortedData = []
//
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
//
const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
for (let i = maxFloor; i >= 1; i--) {
for (let j = 1; j <= maxShelf; j++) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
// ,
sortBookshelvesRightBottom(data) {
const sortedData = []
//
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
const maxShelfPerFloor = data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0]))
.reduce((acc, curr, index, arr) => {
const floor = parseInt(data[index].gridFloor)
if (!acc[floor]) acc[floor] = 1
if (acc[floor] < curr) acc[floor] = curr
return acc
}, {})
//
for (let i = maxFloor; i >= 1; i--) {
//
for (let j = maxShelfPerFloor[i] || 1; j >= 1; j--) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
handleSlidClickFun(index) {
this.cellIndex = null
this.handleSlideToFun(index)
if (localStorage.getItem('bookShelfDetails')) {
this.getInitShelfGridByShelfId(index + 1)
}
},
handleSlideToFun(index) {
this.swiperActiveIndex = index
this.swiperContent.slideTo(index, 500, false)
this.swiperTitle.slideTo(index, 500, false)
},
handleCellCurrent(item, index) {
this.cellIndex = index
this.cellInfo = {
id: item.id,
gridName: item.gridName,
startSortmark: item.startSortmark,
endSortmark: item.endSortmark,
cameraId: item.cameraId,
check: item.isCheck,
order: item.isOrder
}
this.checkValue = `${item.isCheck}`
this.bookSortValue = `${item.isOrder}`
},
handleEditGridNum() {
console.log(this.cellIndex)
if (this.cellIndex !== null) {
this.callNumVisible = true
this.form = this.cellInfo
console.log(this.cellInfo)
} else {
this.$message({ message: '请选择需要操作得层位', type: 'error', offset: 8 })
}
},
handleSaveCallNum() {
if (this.$refs['form']) {
this.$refs['form'].validate((valid) => {
if (valid) {
console.log(this.form)
FetcheEditSortmarkByGrid(this.form).then(res => {
console.log(res)
if (res) {
this.$message({ message: '编辑索书号范围成功', type: 'success', offset: 8 })
this.callNumVisible = false
}
}).catch(() => {
})
}
})
}
},
handleClose() {
if (this.callNumVisible) {
this.$refs['form'].resetFields()
this.callNumVisible = false
}
this.hkVideoVisible = false
},
handleViewVideo(data) {
console.log('data', data)
FetchDeviceById({ 'deviceId': data }).then(res => {
if (res) {
console.log(res)
this.hkConfig = {
'username': res.account,
'password': res.password,
'ip': res.ipv4,
'port': res.rtsp
}
this.hkVideoVisible = true
this.$nextTick(() => {
this.$refs.hkVideoRef.initVideo()
})
}
})
},
//
changeCheckSwitch(data) {
console.log(data)
if (this.cellIndex !== null) {
this.$confirm('此操作将开启/关闭该层位的盘点功能' + '<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
this.cellInfo.check = JSON.parse(data)
console.log(this.cellInfo)
FetchChangeCheckByGrid(this.cellInfo).then(res => {
this.$message({ message: '修改层位盘点状态成功', type: 'success', offset: 8 })
if (localStorage.getItem('bookShelfDetails')) {
this.getInitShelfGridByShelfId(this.swiperActiveIndex + 1)
}
}).catch(() => {
this.checkValue = data === 'true' ? 'false' : 'true'
})
}).catch(() => {
this.checkValue = data === 'true' ? 'false' : 'true'
})
} else {
this.$message({ message: '请选择需要操作得层位', type: 'error', offset: 8 })
this.checkValue = data === 'true' ? 'false' : 'true'
return false
}
},
changeBookSortSwitch(data) {
console.log(data)
if (this.cellIndex !== null) {
this.$confirm('此操作将开启/关闭该层位的图书顺序检查' + '<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
this.cellInfo.order = JSON.parse(data)
FetchChangeOrderByGrid(this.cellInfo).then(res => {
this.$message({ message: '修改图书有序检查状态成功', type: 'success', offset: 8 })
if (localStorage.getItem('bookShelfDetails')) {
this.getInitShelfGridByShelfId(this.swiperActiveIndex + 1)
}
}).catch(() => {
this.bookSortValue = data === 'true' ? 'false' : 'true'
})
}).catch(() => {
this.bookSortValue = data === 'true' ? 'false' : 'true'
})
} else {
this.$message({ message: '请选择需要操作得层位', type: 'error', offset: 8 })
this.bookSortValue = data === 'true' ? 'false' : 'true'
return false
}
},
doExport() {
console.log(this.bookShelfDetails)
this.$confirm('此操作将导出所选数据' + '<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
const params = {
'shelfId': this.bookShelfDetails.id,
'toward': this.bookShelfDetails.toward
}
console.log(params)
const fileName = '层位编码-' + parseTime(new Date()) + '.xlsx'
getBlob(this.baseApi + '/api/bookShelf/exportShelfGridLabel' + '?' + qs.stringify(params, { indices: false }), function(blob) {
saveAs(blob, fileName)
})
}).catch(() => {
})
}
}
}
</script>
<style lang="scss" scoped>
.swiper-title{
::v-deep .swiper-wrapper{
margin: 10px 0;
border-bottom: 1px solid #EDEFF3;
}
}
.swiper-slide-title {
width: auto !important;
margin-right: 20px;
cursor: pointer;
.tab-name {
padding: 10px;
&.active {
color: #0348F3;
border-bottom: 3px solid #0348F3;
}
}
}
.swiper-content{
height: 544px;
}
.view-video{
::v-deep .el-dialog{
width: 1000px !important;
}
}
.swiper-container{
margin: 0 !important;
}
</style>

769
src/views/deviceManage/device/index.vue

@ -0,0 +1,769 @@
<template>
<div class="app-container">
<div class="container-main" style="justify-content: flex-start;">
<div class="elect-cont-left">
<el-tree
ref="tree"
:data="regionTreeData"
:props="defaultProps"
node-key="id"
default-expand-all
:default-checked-keys="defaultCheckedKeys"
:expand-on-click-node="false"
:highlight-current="true"
:render-content="renderContent"
@node-click="handleNodeClick"
/>
</div>
<!--用户数据-->
<div class="elect-cont-right">
<!--工具栏-->
<div class="head-container">
<div class="head-search">
<!-- 搜索 -->
<el-input
v-model="search"
size="small"
clearable
placeholder="输入设备编号或名称搜索"
style="width: 320px;"
class="input-prepend filter-item"
@clear="crud.toQuery"
@keyup.enter.native="crud.toQuery"
>
<el-select slot="prepend" v-model="optionVal" style="width: 120px" @change="searchChange">
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-input>
<!-- <el-input v-model="query.blurry" clearable size="small" placeholder="输入设备编号或名称搜索" prefix-icon="el-icon-search" style="width: 225px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> -->
<el-button class="filter-item filter-search" size="mini" type="success" icon="el-icon-search" @click="crud.toQuery">搜索</el-button>
<el-button class="filter-item filter-refresh" size="mini" type="warning" icon="el-icon-refresh-left" @click="resetQuery()">重置</el-button>
</div>
<crudOperation :permission="permission">
<template v-slot:left>
<el-button v-permission="permission.add" size="mini" :disabled="activeRightBtn" @click="deviceSelectVisible = true">
<i class="iconfont icon-xinzeng" />
新增
</el-button>
<el-button v-permission="permission.edit" size="mini" :disabled="crud.selections.length !== 1" @click="toEdit(crud.selections)">
<i class="iconfont icon-bianji" />
编辑
</el-button>
</template>
<template v-slot:middle>
<el-button slot="reference" size="mini" :loading="crud.delAllLoading" :disabled="crud.selections.length === 0" @click="toDelete(crud.selections)">
<i class="iconfont icon-shanchu" />
删除
</el-button>
</template>
<template v-slot:right>
<!-- :disabled="activeRightBtn" -->
<el-button :disabled="crud.selections.length !== 1 || (crud.selections.length === 1 && crud.selections[0].deviceType !== 1) " type="primary" size="mini" @click="openBindShelf('')"><i class="iconfont icon-bangding" />书架绑定</el-button>
</template>
</crudOperation>
</div>
<div class="container-right">
<span class="right-top-line" />
<span class="left-bottom-line" />
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" height="calc(100vh - 330px)" @row-click="clickRowHandler" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" align="center" width="55" />
<el-table-column prop="deviceType" label="设备类型">
<template slot-scope="scope">
<span v-if="scope.row.deviceType === 1">球型摄像机</span>
<span v-else-if="scope.row.deviceType === 2">盘点机器人</span>
<span v-else-if="scope.row.deviceType === 3">闸机</span>
<span v-else>安全门</span>
</template>
</el-table-column>
<el-table-column prop="deviceName" label="设备名称" />
<el-table-column prop="deviveCode" label="设备编号" />
<!-- <el-table-column prop="ipv4" label="接口IP" /> -->
<el-table-column prop="ipv4" label="接口IP" min-width="120">
<template slot-scope="scope">
<span v-if="scope.row.ipv4"> {{ scope.row.ipv4 }} </span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="端口" prop="rtsp" width="50">
<template slot-scope="scope">
<span v-if="scope.row.rtsp"> {{ scope.row.rtsp }} </span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="状态" prop="deviceState" align="center">
<template slot-scope="scope">
<span v-if="scope.row.deviceState === null" class="el-icon-loading" />
<span v-else :class="{ 'spk-a': scope.row.deviceState === 1, 'off-line': scope.row.deviceState !== 1 }" />
</template>
</el-table-column>
<el-table-column prop="shelfCount" label="已绑架位" />
<el-table-column prop="fondsName" label="所属机构" />
<el-table-column prop="floorName" label="所属楼层" />
<el-table-column prop="regionName" label="所属区域" min-width="120" />
<el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期" width="140">
<template slot-scope="scope">
<div>{{ scope.row.createTime | parseTime }}</div>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
<!-- 设备管理1 -->
<el-dialog
:close-on-click-modal="false"
:modal-append-to-body="false"
append-to-body
:before-close="handleCloseDialog"
:visible.sync="deviceSelectVisible"
title="新增设备-选择设备类型"
>
<div class="setting-dialog">
<el-form ref="deviceForm" inline :model="deviceForm" size="small" label-width="90px">
<el-form-item label="设备类型" prop="deviceType" :rules="[{ required:true, message:'请选择设备类型', trigger:'change'}]">
<el-select v-model="deviceForm.deviceType" class="filter-item" value-key="id" placeholder="设备类型" style="width: 550px;">
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="handleCloseDialog">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="handleComfirmDevice">确定</el-button>
</div>
</div>
</el-dialog>
<!-- 设备管理2 -->
<el-dialog
:close-on-click-modal="false"
:modal-append-to-body="false"
append-to-body
:before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0"
:title="addDeviceTitle"
>
<div class="setting-dialog">
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="90px">
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="form.deviceName" />
</el-form-item>
<el-form-item label="设备编号" prop="deviveCode">
<el-input v-model="form.deviveCode" />
</el-form-item>
<el-row>
<el-form-item label="品牌厂商" prop="deviceBrand">
<el-select v-model="form.deviceBrand" class="filter-item" placeholder="请选择" style="width: 225px;">
<el-option v-for="(item,index) in supplierOptions" :key="index" :label="item.name" :value="item.name" />
</el-select>
</el-form-item>
</el-row>
<el-form-item v-if="selectedDeviceType===1" label="IPv4地址" prop="ipv4">
<el-input v-model="form.ipv4" />
</el-form-item>
<el-form-item v-if="selectedDeviceType===1" label="RTSP端口" prop="rtsp" style="margin-right: 0; margin-left: 30px;">
<el-input v-model="form.rtsp" placeholder="RTSP端口一般均为554" />
</el-form-item>
<el-form-item v-if="selectedDeviceType===1" label="账号" prop="account">
<el-input v-model="form.account" />
</el-form-item>
<el-form-item v-if="selectedDeviceType===1" label="密码" prop="password" style="margin-right: 0; margin-left: 30px;">
<el-input v-model="form.password" />
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="4" style="width: 585px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确定</el-button>
</div>
</div>
</el-dialog>
<!-- 书架绑定 -->
<el-dialog
class="bind-bookShelf-dialog"
:close-on-click-modal="false"
:modal-append-to-body="false"
append-to-body
:before-close="handleCloseDialog"
:visible.sync="bindBookShelfVisible"
title="书架绑定"
>
<div class="setting-dialog">
<el-select v-model="selectShelf" class="filter-item" placeholder="请选择指定架" style="width: 320px; margin-bottom: 20px;" @change="changeShelfBind">
<el-option v-for="(item,index) in bindReigonsTree" :key="index" :label="item.shelfName" :value="item.shelfId" />
</el-select>
<div class="transfer-bookshelf">
<el-transfer
v-model="bindGirdData"
style="text-align: left; display: inline-block"
filterable
filter-placeholder="请输入关键字检索"
:titles="['未绑定 - 层位架', '已绑定 - 层位架']"
:button-texts="['移除', '加入']"
:left-default-checked="leftDefaultChecked"
:right-default-checked="rightDefaultChecked"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}'
}"
:data="unBindGirdData"
@change="handleChange"
>
<div slot-scope="{ option }" style="display: flex;">
<!-- <span style="display:block; width: 50px; text-align: center;">{{ option.key }}</span> -->
<span style="display:block; width: 150px;">{{ option.label }}</span>
</div>
</el-transfer>
<el-button class="transfer-footer" :disabled="bindGirdData.length===0" size="small" @click="rightCheckedClear"><i class="iconfont icon-shanchu" />清空</el-button>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="handleCloseDialog">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="dialogSaveSeting">确定</el-button>
</div>
</div>
</el-dialog>
</div>
</div>
</div>
</template>
<script>
import crudDevice from '@/api/deviceVI/index'
import { FetchInitBookShelfList } from '@/api/shelf/index'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
const defaultForm = {
'account': null,
'deviceBrand': null,
'deviceName': null,
'deviceType': null,
'deviveCode': null,
'floorId': null,
'fondsId': null,
'id': null,
'ipv4': null,
'password': null,
'regionId': null,
'remarks': null,
'rtsp': null
}
export default {
name: 'Device',
components: { crudOperation, pagination },
cruds() {
return CRUD({ title: '设备', url: 'api/device/initDeviceInfoList', idField: 'id || deviceId', sort: [], crudMethod: { ...crudDevice }, optShow: {
add: false,
edit: false,
del: false,
reset: true,
download: false,
group: false
},
queryOnPresenterCreated: false
})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
regionTreeData: null,
defaultProps: {
children: 'children',
label: 'label'
},
defaultCheckedKeys: [],
selectedTreeKey: null,
search: null,
optionVal: 1,
options: [
{
'id': 1,
'name': '球型摄像机'
},
{
'id': 2,
'name': '盘点机器人'
},
{
'id': 3,
'name': '闸机'
},
{
'id': 4,
'name': '安全门'
}
],
activeRightBtn: false,
bindBookShelfVisible: false,
fondsDatas: [],
deviceSelectVisible: false,
deviceForm: {
deviceType: null
},
addDeviceTitle: '',
supplierOptions: [
{ 'name': '海康威视' },
{ 'name': '大华' },
{ 'name': '天地伟业' },
{ 'name': '其他' }
],
selectedDeviceType: '球型摄像机',
levelNumber: 0,
btn: 'add',
unBindGirdData: [],
bindGirdData: [],
leftDefaultChecked: [],
rightDefaultChecked: [],
permission: {
add: ['admin', 'device:add'],
edit: ['admin', 'device:edit'],
del: ['admin', 'device:del']
},
floorTreeValue: null,
bindReigonsTree: [],
selectShelf: null
}
},
computed: {
rules() {
const checkDevicePort = (rule, value, callback) => {
const reg = /^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/
if (reg.test(value)) {
callback()
} else {
callback(new Error('端口号输入错误'))
}
}
const validateRule = {
deviveCode: [{ required: true, message: '设备编号不可为空', trigger: 'blur' }],
deviceName: [{ required: true, message: '设备名称不可为空', trigger: 'blur' }]
}
this.$set(validateRule, 'ipv4', [
{ required: true, message: '请输入接口IP', trigger: 'blur' }
])
this.$set(validateRule, 'rtsp', [
{ required: true, message: '请输入端口号', trigger: 'blur' },
{ validator: checkDevicePort, trigger: 'blur' }
])
this.$set(validateRule, 'account', [
{ required: true, message: '请输入账号', trigger: 'blur' }
])
this.$set(validateRule, 'password', [
{ required: true, message: '请输入密码', trigger: 'blur' }
])
return validateRule
}
},
watch: {
},
created() {
this.deviceTypeOptions = this.options
crudDevice.FetchRegionTree().then(res => {
this.regionTreeData = [this.transformData(res)]
this.expandDefault()
}).catch(() => {
})
},
mounted() {
},
methods: {
normalizer(node) {
return {
id: node.id,
label: node.label,
children: node.children,
isDisabled: !node.regionId
}
},
// tree
transformData(data) {
return {
id: data.fondsId,
fondsId: data.fondsId,
label: data.fondsName,
children: data.floors.map(floor => ({
id: floor.id,
floorId: floor.id,
label: floor.floorName,
children: floor.regions.map(region => ({
id: region.id,
regionId: region.id,
label: region.regionName,
parentFloorId: floor.id
}))
}))
}
},
treeSelectInput(value) {
console.log('treeSelectInput', value)
},
renderContent(h, { node, data, store }) {
return h('span', [
h('el-tooltip', {
props: {
content: data.label || '',
placement: 'top'
}
}, [
h('span', node.label)
])
])
},
expandDefault() {
// https://blog.csdn.net/weixin_47218354/article/details/135261449
this.defaultCheckedKeys.push(this.regionTreeData[0].id)
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.regionTreeData[0].id) // ()
this.handleNodeClick(this.regionTreeData[0])
})
},
//
[CRUD.HOOK.beforeRefresh]() {
this.crud.query.search = this.search
this.crud.query.deviceType = this.optionVal
},
[CRUD.HOOK.afterRefresh](crud) {
crud.data.forEach(element => {
element.deviceState = null
this.getDeviceState(element)
})
},
getDeviceState(element) {
crudDevice.FetchPingIP({ ip: element.ipv4 }).then((data) => {
element.deviceState = data === true ? 1 : 0
}).catch((error) => {
console.error(error)
})
},
handleNodeClick(data) {
this.$nextTick(() => {
if (this.$refs.tree) {
this.selectedTreeKey = this.$refs.tree.getCurrentNode()
this.crud.query.fondsId = this.regionTreeData[0].id
if (this.selectedTreeKey.hasOwnProperty('floorId')) {
this.crud.query.floorId = this.selectedTreeKey.id
} else if (this.selectedTreeKey.hasOwnProperty('regionId')) {
this.crud.query.floorId = this.selectedTreeKey.parentFloorId
} else {
this.crud.query.floorId = null
}
this.crud.query.deviceType = this.optionVal
this.search = ''
this.crud.query.regionId = this.selectedTreeKey.hasOwnProperty('regionId') ? this.selectedTreeKey.id : null
if (!this.selectedTreeKey.hasOwnProperty('regionId')) {
this.activeRightBtn = true
} else {
this.activeRightBtn = false
}
this.crud.toQuery()
const params = {
'floorId': this.crud.query.floorId,
'regionId': this.crud.query.regionId
}
FetchInitBookShelfList(params).then(res => {
this.bindReigonsTree = res.content
}).catch(() => {
})
}
})
},
searchChange(val) {
if (val) {
this.search = ''
}
},
resetQuery() {
this.search = null
this.optionVal = 1
this.crud.query.deviceType = this.optionVal
this.crud.query.search = this.search
this.crud.toQuery()
},
//
[CRUD.HOOK.beforeToAdd](crud, form) {
this.crud.form.fondsId = this.regionTreeData[0].id
this.crud.form.deviceType = this.selectedDeviceType
if (this.selectedTreeKey.hasOwnProperty('floorId')) {
this.crud.query.floorId = this.selectedTreeKey.id
} else if (this.selectedTreeKey.hasOwnProperty('regionId')) {
this.crud.query.floorId = this.selectedTreeKey.parentFloorId
} else {
this.crud.query.floorId = null
}
this.crud.form.regionId = this.selectedTreeKey.hasOwnProperty('regionId') ? this.selectedTreeKey.id : null
},
toEdit(row) {
crudDevice.FetchDeviceById({ 'deviceId': row[0].deviceId }).then(res => {
if (res) {
crud.form = res
this.crud.toEdit(crud.form)
}
})
},
[CRUD.HOOK.beforeToEdit](crud, form, btn) {
this.selectedDeviceType = this.form.deviceType
this.btn = 'edit'
let deviceName = ''
console.log('form', this.form.deviceType)
if (this.form.deviceType === 1) {
deviceName = '球形摄像机'
} else if (this.form.deviceType === 2) {
deviceName = '盘点机器人'
} else if (this.form.deviceType === 3) {
deviceName = '闸机'
} else if (this.form.deviceType === 4) {
deviceName = '安全门'
} else {
deviceName = ''
}
this.addDeviceTitle = '编辑设备 - ' + deviceName
},
//
[CRUD.HOOK.afterValidateCU](crud) {
if (this.btn === 'add') {
this.crud.form.deviceType = this.selectedDeviceType
this.crud.form.floorId = this.selectedTreeKey.parentFloorId
this.crud.form.fondsId = this.regionTreeData[0].id
this.crud.form.regionId = this.selectedTreeKey.id
}
delete crud.form.fondsName
delete crud.form.floorName
delete crud.form.deviceState
delete crud.form.shelfCount
delete crud.form.regionName
return true
},
clickRowHandler(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row)
},
//
toDelete(datas) {
this.$confirm('此操作将删除当前所选' + this.crud.title + '<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
this.crud.delAllLoading = true
const ids = []
datas.forEach(val => {
ids.push(val.deviceId)
})
crudDevice.del(ids).then(() => {
this.crud.notify('删除成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
this.crud.delAllLoading = false
this.crud.refresh()
}).catch(err => {
this.crud.delAllLoading = false
console.log(err)
})
}).catch(() => {
})
},
handleComfirmDevice() {
this.$refs.deviceForm.validate((valid) => {
if (valid) {
this.btn = 'add'
this.deviceSelectVisible = false
this.selectedDeviceType = this.deviceForm.deviceType.id
this.addDeviceTitle = '新增设备 - ' + this.deviceForm.deviceType.name
this.crud.toAdd()
} else {
console.log('error submit!!')
return false
}
})
},
handleCloseDialog(done) {
this.bindBookShelfVisible = false
//
if (this.$refs.deviceForm) {
this.deviceSelectVisible = false
this.$refs.deviceForm.resetFields()
this.deviceForm.deviceType = null
}
if (this.$refs.form) {
this.crud.cancelCU()
this.$refs.form.resetFields()
}
//
// done()
},
changeShelfBind(val) {
this.openBindShelf(val)
},
openBindShelf(shelfId) {
crudDevice.FetchDeviceById({ 'deviceId': this.crud.selections[0].deviceId }).then(res => {
this.bindBookShelfVisible = true
const params = {
'fondsId': res.fondsId,
'floorId': res.floorId,
'regionId': res.regionId,
'shelfId': shelfId
}
const params2 = {
'deviceId': res.id
}
Promise.all([
crudDevice.FetchUnboundGrid(params),
crudDevice.FetchBoundGridByDevice(params2)
]).then(results => {
console.log('FetchUnboundGrid result:', results[0])
console.log('FetchBoundGridByDevice result:', results[1])
this.unBindGirdData = results[0].map((item, index) => {
return {
id: item.gridId,
label: item.gridName,
key: item.gridId,
shelfId: item.shelfId
}
})
if (results[1].length !== 0) {
this.unBindGirdData = [
...this.unBindGirdData,
...results[1].map((item, index) => {
return {
id: item.gridId,
label: item.gridName,
key: item.gridId,
shelfId: item.shelfId
}
})
]
const movedKeys = results[1].map(item => item.gridId)
this.bindGirdData = movedKeys
} else {
this.bindGirdData = []
}
}).catch(error => {
console.error('Error fetching grids:', error)
})
})
},
handleChange(value, direction, movedKeys) {
console.log(value + direction + movedKeys)
},
dialogSaveSeting() {
// https://www.cnblogs.com/liujiajiablog/p/15818056.html
const movedIds = this.bindGirdData.map(key => {
const item = this.unBindGirdData.find(item => item.key === key)
return item ? item.id : null
})
const params = {
'deviceId': this.crud.selections[0].deviceId,
'shelfGridIds': movedIds
}
crudDevice.FetchDeviceShelfGridBinding(params).then(res => {
if (res) {
this.$message({ message: res, type: 'success', offset: 8 })
this.crud.refresh()
}
this.bindBookShelfVisible = false
})
},
rightCheckedClear() {
this.bindGirdData = []
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .input-prepend .el-input__inner{
padding-left: 130px;
}
::v-deep .el-tree{
.el-tree-node__content{
font-size: 14px;
color: #545B65;
.tree-text{
font-size: 16px;
font-weight: 600;
color: #0C0E1E;
}
}
.el-tree-node__children{
.tree-text {
font-size: 14px !important;
font-weight: normal;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
display: inline-block;
min-width: 170px;
color: #545B65;
}
}
}
.bind-bookShelf-dialog{
::v-deep .el-dialog{
width: 820px;
}
}
.transfer-bookshelf{
position: relative;
::v-deep .el-transfer{
display: flex !important;
justify-content: space-between;
align-items: center;
.el-transfer-panel{
width: 400px !important;
.el-transfer-panel__body{
height: 480px;
.el-transfer-panel__list.is-filterable{
height: 420px;
}
.el-transfer-panel__filter .el-input__inner{
border-radius: 4px;
}
.el-transfer-panel__filter .el-input__icon{
margin-left: 0;
}
}
}
.el-transfer__buttons{
display: flex;
flex-direction: column;
.el-button:last-child{
margin-left: 0;
.el-icon-arrow-right:before{
color: #0348f3;
}
}
.el-button.is-disabled{
.el-icon-arrow-right:before{
color: #b4c8fb;
}
}
}
}
.transfer-footer{
position: absolute;
left: 50%;
bottom: 185px;
transform: translateX(-50%);
}
}
</style>

282
src/views/deviceManage/floor/index.vue

@ -0,0 +1,282 @@
<template>
<div class="app-container">
<div class="venue-header">
<h4><i class="iconfont icon-duolouceng" />楼层列表</h4>
<p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
</div>
<div class="venue-content">
<div class="venue-left">
<div class="head-container">
<div class="head-search">
<!-- 搜索 -->
<el-input v-model="query.search" clearable size="small" placeholder="输入关键字搜索" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @clear="crud.toQuery" @keyup.enter.native="crud.toQuery" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<template v-slot:middle>
<el-button slot="reference" size="mini" :loading="crud.delAllLoading" :disabled="crud.selections.length === 0" @click="toDelete(crud.selections)">
<i class="iconfont icon-shanchu" />
删除
</el-button>
</template>
<template v-slot:right>
<el-button :loading="crud.downloadLoading" size="mini" :disabled="crud.selections.length <= 1" @click="showSort">
<i class="iconfont icon-paixu" />
排序
</el-button>
</template>
</crudOperation>
</div>
<div style="position: relative;">
<div class="double-click-btn"><i class="iconfont icon-zhuyi-lan" /><span>双击列表数据查看对应区域</span></div>
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
style="width: 100%;"
height="506"
@selection-change="crud.selectionChangeHandler"
@row-click="clickRowHandler"
@row-dblclick="onRowDblclick"
>
<el-table-column type="selection" align="center" width="55" />
<el-table-column type="index" label="排序" />
<el-table-column prop="floorName" label="楼层名称" />
<el-table-column prop="regionCount" label="区域" />
<el-table-column prop="floorMap" label="地图">
<template slot-scope="scope">
<span :class="['row-state', scope.row.floorMap ? 'end-state' : 'cancel-state' ]">{{ scope.row.floorMap ? '已上传': '未上传' }}</span>
</template>
</el-table-column>
<!-- <el-table-column prop="floorDescription" label="说明">
<template slot-scope="scope">
<div>{{ scope.row.floorDescription ? scope.row.floorDescription : '-' }}</div>
</template>
</el-table-column> -->
<!-- <el-table-column prop="createTime" label="创建时间" min-width="180">
<template slot-scope="scope">
<div>{{ scope.row.createTime | parseTime }}</div>
</template>
</el-table-column> -->
</el-table>
<!--分页组件-->
<pagination v-if="crud.data.length!==0" />
</div>
</div>
<div class="venue-right">
<div class="container-right tab-content">
<span class="right-top-line" />
<span class="left-bottom-line" />
<ul class="tab-nav">
<li class="active-tab-nav">楼层预览<i /></li>
<!-- 最右侧装饰img -->
<span class="tab-right-img" />
</ul>
<!-- v-loading="imgLoading" -->
<div class="venue-preview">
<!-- <img src="~@/assets/images/system/default-img.jpg" alt=""> -->
<img :src="imageUrl" :onerror="defaultImg" alt="">
</div>
</div>
</div>
</div>
<!-- form -->
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<el-form ref="form" :rules="rules" :model="form" size="small" label-width="100px">
<el-form-item label="楼层名称" prop="floorName">
<el-input v-model="form.floorName" style="width: 580px;" />
</el-form-item>
<el-form-item label="描述信息" prop="floorDescription">
<el-input v-model="form.floorDescription" placeholder="请输入" type="textarea" rows="3" style="width: 580px;" />
</el-form-item>
<UploadCover :label-name="labelName" :form="form" upload-type="other" @childCover="handleCover" />
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">保存</el-button>
</div>
</div>
</el-dialog>
<!-- 排序 -->
<el-dialog :close-on-click-modal="false" :append-to-body="true" title="排序" :visible.sync="sortVisible" @opened="opened">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<i class="drag-tip">提示请通过拖动鼠标来调整当前顺序</i>
<el-table :data="sortTableData" class="file-sort" style="width: 100%;max-height: 70vh;" row-key="id">
<el-table-column type="index" label="序号" width="100" align="center" />
<el-table-column prop="floorName" label="楼层名称" />
</el-table>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click.native="handleSort">保存</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import crudFloor from '@/api/floor/index'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import rrOperation from '@crud/RR.operation'
import pagination from '@crud/Pagination'
import UploadCover from '@/views/components/upload.vue'
import Sortable from 'sortablejs'
import { mapGetters } from 'vuex'
import defaultImg from '@/assets/images/system/default-img.jpg'
const defaultForm = { id: null, floorName: null, floorDescription: null, floorMap: null }
export default {
name: 'Floor',
components: { crudOperation, rrOperation, pagination, UploadCover },
cruds() {
return CRUD({ title: '楼层', url: 'api/libraryFloor/initLibraryFloorList', crudMethod: { ...crudFloor }, sort: [], optShow: {
add: true,
edit: true,
del: false,
download: false,
group: false,
reset: false
}})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
imgLoading: false,
labelName: '楼层地图',
permission: {
add: ['admin', 'floor:add'],
edit: ['admin', 'floor:edit'],
del: ['admin', 'floor:del']
},
rules: {
floorName: [
{ required: true, message: '所在楼层不可为空', trigger: 'blur' }
]
},
defaultImg: defaultImg,
imageUrl: defaultImg,
sortTableData: [], // data
sortVisible: false // dialog
}
},
computed: {
...mapGetters([
'user',
'baseApi'
])
},
methods: {
[CRUD.HOOK.beforeRefresh]() {
},
[CRUD.HOOK.afterRefresh](crud) {
console.log(crud.data)
if (crud.data.length !== 0) {
this.clickRowHandler(crud.data[0])
}
},
//
[CRUD.HOOK.afterValidateCU](crud) {
console.log(crud.form)
return true
},
handleCover(value) {
console.log('value', value)
this.crud.form.floorMap = value
},
clickRowHandler(row) {
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row)
// http://192.168.99.67:12010/api/fileRelevant/getImg?imgType=1&imgId=f6d3ecea-0456-4429-ba77-1a4921d5c806
if (row.floorMap) {
this.imageUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=1&imgId=' + row.floorMap
} else {
this.imageUrl = this.defaultImg
}
},
onRowDblclick(row) {
// crudShelf.FetchBookShelfDetails({ 'shelfId': row.shelfId }).then(res => {
// this.$router.push({ path: '/bookshelf/bookshelfPosition', query: { 'floorName': row.floorName, 'regionName': row.regionName }})
// localStorage.setItem('bookShelfDetails', JSON.stringify(res))
// }).catch(() => {
// })
localStorage.setItem('formFloor', JSON.stringify(row))
this.$router.push({ path: '/deviceManage/area', query: { 'formFloor': true }})
},
toDelete(datas) {
this.$confirm('此操作将删除当前所选楼层<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
this.crud.delAllLoading = true
const ids = []
datas.forEach(val => {
ids.push(val.id)
})
console.log(ids)
crudFloor.del(ids).then(res => {
console.log(res)
this.$message({ message: res, type: 'success', offset: 8 })
this.crud.delAllLoading = false
this.crud.refresh()
}).catch(err => {
this.crud.delAllLoading = false
console.log(err)
})
}).catch(() => {
this.crud.delAllLoading = false
})
},
// -
rowDrop(className, targetName) {
//
const tbody = document.querySelector('.' + className + ' .el-table__body-wrapper tbody')
const that = this
Sortable.create(tbody, {
//
draggable: '.el-table__row',
onEnd({ newIndex, oldIndex }) {
if (newIndex === oldIndex) return
that[targetName].splice(newIndex, 0, that[targetName].splice(oldIndex, 1)[0])
}
})
},
//
opened() {
this.rowDrop('file-sort', 'sortTableData')
},
showSort() {
this.sortVisible = true
this.sortTableData = JSON.parse(JSON.stringify(this.crud.selections))
},
handleSort() {
const data = this.sortTableData.map((value, index) => {
return { id: value.id, isSequence: index + 1 }
})
this.sortTableData.forEach((item, index) => {
item.isSequence = index + 1
})
crudFloor.sort(data).then(() => {
this.sortVisible = false
this.$message({ message: '保存成功', type: 'success', offset: 8 })
this.crud.refresh()
})
}
}
}
</script>
<style lang="scss" scoped>
.tab-content{
min-height: calc(100vh - 232px) !important;
}
</style>

16
src/views/deviceManage/index.vue

@ -0,0 +1,16 @@
<template>
<div>
<router-view />
</div>
</template>
<script>
export default {
name: 'VenueDevice',
data() {
return {
}
}
}
</script>
<style scoped>
</style>

24
src/views/faceRecognition/personInfoManage.vue

@ -78,7 +78,7 @@
<img :src="imageUrl || require('@/assets/images/user.jpg')" alt="人员照片">
<div class="upload-btn" style="margin: 0 0 10px 0;">
<input id="upFile" type="file" name="upFile" accept="image/*" @change="changeFile($event)">
<el-button size="small" type="primary"><i class="iconfont icon-shangchuan" />选择上传照片</el-button>
<el-button ref="uploadBtn" size="small" type="primary" @click="handleButtonClick"><i class="iconfont icon-shangchuan" />选择上传照片</el-button>
</div>
<!-- <el-button type="primary" @click="showCamera"><i class="iconfont icon-yulan" />摄像头拍摄</el-button> -->
</div>
@ -287,6 +287,10 @@ export default {
console.log(crud.form)
return true
},
handleButtonClick() {
// file input
document.getElementById('upFile').click()
},
async changeFile(e) {
const file = e.target.files[0]
@ -471,4 +475,22 @@ export default {
border-color: #909399;
color: #909399;
}
.upload-btn{
display: flex;
flex-direction: column;
align-items: center;
#upFile{
position: static;
left: 0;
top: 0;
opacity: 0;
height: 34px;
}
.el-button{
position: static;
margin-top: -34px;
z-index: 99;
}
}
</style>

18
src/views/faceRecognition/personRegister.vue

@ -462,4 +462,22 @@ export default {
border-color: #909399;
color: #909399;
}
.upload-btn{
display: flex;
flex-direction: column;
align-items: center;
#upFile{
position: static;
left: 0;
top: 0;
opacity: 0;
height: 34px;
}
.el-button{
position: static;
margin-top: -34px;
z-index: 99;
}
}
</style>
Loading…
Cancel
Save