Commit b4bfa343 by 李君

匹配精度

1 parent ef981a17
window._serverHost = ['localhost', '192.168.1.165'].includes(window.location.hostname) ? '36.112.68.214:33333' : window.location.host
window._baseUrl = ['localhost', '192.168.1.165'].includes(window.location.hostname) ? 'http://36.112.68.214:33333/btool/' : `https://${window._serverHost}/btool/`
window._baseImgUrl = ['localhost', '192.168.1.39'].includes(window.location.hostname) ? 'https://store.keliuyun.com/images/' : `${window.location.origin}/images/`
window._socketUrl = ['localhost', '192.168.1.165'].includes(window.location.hostname) ? `wss://${window._serverHost}/` : `wss://${window._serverHost}/`
window._serverHost = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? 'https://mall.keliuyun.com' : window.location.host
window._baseUrl = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? 'https://mall.keliuyun.com/btool/' : `https://${window._serverHost}/btool/`
window._baseImgUrl = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? 'https://mall.keliuyun.com/images/' : `${window.location.origin}/images/`
window._socketUrl = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? `wss://${window._serverHost}/` : `wss://${window._serverHost}/`
const log = console.log.bind(console)
......@@ -14,14 +14,14 @@ const axiosInstance = axios.create(
// 请求拦截器
axiosInstance.interceptors.request.use(
config => {
// Cookies.set('atoken','d697b325-3a0d-4364-b851-a27d8b6e2a1e')
// Cookies.set('atoken','57ac28e0-c2d9-4287-8d20-bc0332372dad')
if(!Cookies.get('atoken')){
ElMessage({
message: `登录过期,请重新登录`,
type: 'warning'
})
setTimeout(()=>{
let url = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? 'https://store.keliuyun.com/' : window.location.origin
let url = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? 'https://mall.keliuyun.com/' : window.location.origin
// if(url.includes('36.112.68.214')){
// url = 'http://36.112.68.214:33333/'
// }
......@@ -45,7 +45,7 @@ axiosInstance.interceptors.response.use(
type: 'warning'
})
setTimeout(()=>{
let url = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? 'https://store.keliuyun.com/' : window.location.origin
let url = ['localhost', '192.168.1.28'].includes(window.location.hostname) ? 'https://mall.keliuyun.com/' : window.location.origin
// if(url.includes('36.112.68.214')){
// url = 'http://36.112.68.214:33333/'
// }
......
......@@ -61,6 +61,10 @@ const menuRoute = [
path: 'equipmentDataRetransmission',
component: () => import("@/views/equipmentDataRetransmission/equipmentDataRetransmission.vue"),
},
{
path: 'FeatureMatchingAccuracy',
component: () => import("@/views/FeatureMatchingAccuracy/FeatureMatchingAccuracy.vue")
},
]
},
]
......
<template>
<div class="containter">
<el-row class="contentBox" v-loading="isLoading">
<el-col :span="7" class="box boxLeft">
<a-form :model="queryForm" layout="inline" :label-col="{ style: { width: '70px' } }">
<a-form-item label="集团:">
<a-select v-model:value="queryForm.account_id" style="width: 150px" :options="accountList" @change="onAccountChange" optionFilterProp="label" show-search>
</a-select>
</a-form-item>
<a-form-item label="广场:">
<a-select v-model:value="queryForm.plaza_id" style="width: 150px" :options="plazaList" @change="onMallChange" optionFilterProp="label" show-search>
</a-select>
</a-form-item>
</a-form>
<div class="btns">
<a-button type="primary" @click="confirmSearch" class="btn">搜索</a-button>
<a-button type="primary" @click="addPersonFun" class="btn">新增注册人员</a-button>
<!-- <a-button type="primary" @click="confirmSearch" class="btn">预览报告</a-button> -->
</div>
<el-table :data="resultList" @row-click='clickRow' :highlight-current-row='true' :rowKey="id" :height="contentHeight" style="width: 100%">
<el-table-column type="index" align="center" label="序号" width="80">
<template #default="scope">
<span v-text="getIndex(scope.$index)"> </span>
</template>
</el-table-column>
<el-table-column prop="name" align="center" label="姓名">
</el-table-column>
<el-table-column prop="regCount" align="center" label="注册图片数量">
</el-table-column>
<el-table-column prop="hitCount" align="center" label="命中目标数量">
</el-table-column>
<el-table-column prop="content" align="center" label="操作" width="150">
<template #default="scope">
<el-button
@click.stop
@click="editRow(scope.row)"
type="text"
size="small"
class="tab-btn"
>编辑</el-button>
<el-button
@click.stop
@click="deleteRow(scope.row)"
type="text"
size="small"
class="tab-btn"
>删除</el-button>
<el-button
@click.stop
@click="hitDetail(scope.row)"
type="text"
size="small"
class="tab-btn"
>命中详情</el-button>
</template>
</el-table-column>
</el-table>
<a-pagination
v-model:current="pageNum"
v-model:pageSize="pageSize"
:total="total"
:show-total="total => `共 ${total} 条`"
:pageSizeOptions="['10', '20', '50']"
@change="onPageNumChange"
@showSizeChange="onPageSizeChange"
show-size-changer
show-quick-jumper
style="text-align:center"
/>
</el-col>
<el-col :span="17" class="box boxRight">
<div style="margin-bottom: 10px;min-height:350px">
<a-tabs type="card" v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="注册样本" class="regBox">
<div>注册样本图:{{regPictureList.length}}</div>
<el-row :gutter="10">
<el-col :span="3" v-for="item in regPictureList">
<div style="cursor: pointer;" class='hitBox' @click="clickPicture(item)" :class="currentId==item.id?'active':''">
<el-image :src="item.picture_url"
:fit="'fill'"
class="single-image">
</el-image>
<span class="el-icon-delete delHitPic" @click.stop @click="delSamplePic(item)"></span>
</div>
</el-col>
</el-row>
</a-tab-pane>
<a-tab-pane key="2" tab="命中目标" class="regBox">
<p>命中目标图:{{hitPictureList.length}}</p>
<el-row :gutter="10">
<el-col :span="3" v-for="item in hitPictureList">
<div style="cursor: pointer;" class='hitBox' @click="clickPicture(item)" :class="currentId==item.id?'active':''">
<el-image :src="item.picture_url"
:fit="'fill'"
class="single-image">
</el-image>
<span class="el-icon-delete delHitPic" @click.stop @click="delHitPic(item)"></span>
</div>
</el-col>
</el-row>
</a-tab-pane>
</a-tabs>
</div>
<a-form :model="hitSearchObj" layout="inline" :label-col="{ style: { width: 'auto' } }">
<a-form-item label="监控点:" class="secondCondition">
<a-select v-model:value="hitSearchObj.gate_id"
style="width: 250px"
mode="multiple"
:maxTagCount="1"
:options="gateList"
optionFilterProp="label"
show-search
>
</a-select>
</a-form-item>
<a-form-item label="方向:" class="secondCondition">
<a-select v-model:value="hitSearchObj.direction"
mode="multiple"
:maxTagCount="1"
style="width: 125px">
<a-select-option :value="1"></a-select-option>
<a-select-option :value="-1"></a-select-option>
</a-select>
</a-form-item>
<a-form-item label="分数:" class="secondCondition">
<a-input v-model:value="hitSearchObj.score" style="width: 50px;"></a-input>
</a-form-item>
<a-form-item label="选择日期:" class="secondCondition">
<a-date-picker v-model:value="hitSearchObj.date" :format="'YYYY-MM-DD'" style="width: 110px"/>
</a-form-item>
<a-form-item label="选择时间:" class="secondCondition">
<a-time-picker v-model:value="hitSearchObj.startTime" style="width: 90px"/>
<a-time-picker v-model:value="hitSearchObj.endTime" style="width: 90px"/>
</a-form-item>
<a-form-item class="secondCondition">
<a-button type="primary" style="margin-right: 5px;" @click="searchPicture">查询</a-button>
<a-button type="primary" @click="checkPicture">选中</a-button>
</a-form-item>
</a-form>
<div class="identifyResult">
<div>识别结果:{{identifyResultList.length}}</div>
<el-row :gutter="10" class="identifyResultTable" :style="{height:contentHeight-340+'px'}">
<el-col :span="3" v-for="item in identifyResultList">
<div class="pictureBox">
<el-image :src="item.picture_url"
:fit="'fill'"
class="single-image">
</el-image>
<el-checkbox class="checkBox" v-model="item.checked"></el-checkbox>
<span class="score">{{item.featureNum}}</span>
<span class="text" @click="clickPicture(item)">
<span class="el-icon-picture-outline iconPic"></span>
</span>
</div>
</el-col>
</el-row>
<a-pagination
v-model:current="pageNum_identify"
v-model:pageSize="pageSize_identify"
:total="total_identify"
:show-total="total => `共 ${total_identify} 条`"
:pageSizeOptions="['16', '32', '64']"
@change="onPageNumChange_identify"
@showSizeChange="onPageSizeChange_identify"
show-size-changer
show-quick-jumper
style="text-align:center"
/>
</div>
</el-col>
</el-row>
<addPersonConfig ref="addPersonConfigRef" @refreshParentTable = 'confirmSearch'></addPersonConfig>
<editPersonConfig ref="editPersonConfigRef" @refreshParentTable = 'confirmSearch'></editPersonConfig>
<hitDetailDialog ref="hitDetailDialogRef" @refreshParentTable = 'confirmSearch'></hitDetailDialog>
</div>
</template>
<script>
import {
reactive,
ref,
toRaw
} from 'vue'
import moment from 'moment'
import snapshotRecordApi from '@/views/SnapshotCluster/SnapshotRecord/SnapshotRecordApi'
import {
isArray
} from '@/PublicUtil/Judgment'
import {
filterEmptyValueInObject,
formatDate,
formatTime
} from '@/PublicUtil/PublicUtil'
import FeatureMatchingAccuracyApi from '@/views/FeatureMatchingAccuracy/FeatureMatchingAccuracyApi'
import {
PlusOutlined
} from '@ant-design/icons-vue'
import addPersonConfig from './addPerson.vue'
import editPersonConfig from './editPerson.vue'
import hitDetailDialog from './hitDetailDialog.vue'
import {ElMessage,ElMessageBox} from 'element-plus'
export default {
components:{
addPersonConfig,
editPersonConfig,
hitDetailDialog
},
setup() {
const accountList = ref([])
const plazaList = ref([])
const queryForm = reactive({
account_id: '',
plaza_id: '',
})
const hitSearchObj = reactive({
gate_id: [],
score:75,
direction: [1, -1],
date: moment(moment().format('YYYY-MM-DD'), 'YYYY-MM-DD'),
startTime: moment('00:00:00', 'HH:mm:ss'),
endTime: moment('23:59:59', 'HH:mm:ss'),
})
const onAccountChange = function() {
getPlazaList()
}
const getPlazaList = function() {
queryForm.plaza_id = ''
plazaList.value = []
snapshotRecordApi.getPlazaList({
account_id: queryForm.account_id
}).then(
(r) => {
if (isArray(r)) {
for (const item of r) {
plazaList.value.push({
value: item.id,
label: item.name,
})
}
queryForm.plaza_id = r[0].id
getGateList()
confirmSearch()
}
}
)
}
const getAccountList = function() {
queryForm.account_id = ''
accountList.value = []
snapshotRecordApi.getAccountList().then(
(r) => {
if (isArray(r)) {
for (const item of r) {
accountList.value.push({
value: item.id,
label: item.name,
})
}
queryForm.account_id = r[0].id
getPlazaList()
}
}
)
}
const onMallChange = function(){
getGateList()
}
const gateList = ref([])
const getGateList = function() {
hitSearchObj.gate_id = []
gateList.value = []
snapshotRecordApi.getGateList(
{
account_id: queryForm.account_id,
plaza_id: queryForm.plaza_id,
type: 0,
}
).then(
(r) => {
if (isArray(r.data))
{
for (const item of r.data)
{
gateList.value.push(
{
value: item.id,
label: item.name,
}
)
}
}
}
)
}
const resultList = ref([])
const confirmSearch = function() {
pageNum.value = 1;
getTableData()
}
const getTableData = function(){
resultList.value = []
const data = filterEmptyValueInObject(
{
pageNum:pageNum.value - 1,
pageSize:pageSize.value,
accountId: queryForm.account_id,
mallId: queryForm.plaza_id
}
)
FeatureMatchingAccuracyApi.getMatch(data).then(
(r) => {
if(r.msg_code==200){
resultList.value = r.data.records;
total.value = r.data.total;
}
}
)
}
const onPageNumChange = function(num) {
pageNum.value = num
confirmSearch()
}
const onPageSizeChange = function(current, size) {
pageNum.value = 1
pageSize.value = size
confirmSearch()
}
const isLoading = ref(false)
const pageNum = ref(1)
const pageSize = ref(10)
const total = ref()
const contentHeight = ref(0)
const addPersonConfigRef = ref();
const addPersonFun = function(){
addPersonConfigRef.value.initDialog({
account_id:queryForm.account_id,
plaza_id:queryForm.plaza_id,
});
}
const editPersonConfigRef = ref();
const editRow = function(row){
FeatureMatchingAccuracyApi.getMatchOne(row).then(
(r) => {
if(r.msg_code==200){
editPersonConfigRef.value.initDialog(r.data);
}else{
ElMessage({
message: r.msg_info,
type: 'error'
})
return false;
}
}
)
}
const deleteRow = function(row){
ElMessageBox.confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
isLoading.value = true;
FeatureMatchingAccuracyApi.delMatch(row).then(
(r) => {
isLoading.value = false;
if(r.msg_code==200){
ElMessage({
message: r.msg_info,
type: 'success'
})
confirmSearch()
}else{
ElMessage({
message: r.msg_info,
type: 'error'
})
return false;
}
}
)
})
}
const hitDetailDialogRef = ref()
const hitDetail = function(row){
const data = filterEmptyValueInObject(
{
accountId: row.accountId,
mallId: row.mallId,
id:row.id
}
)
FeatureMatchingAccuracyApi.getMatchHitDetail(data).then(
(r) => {
if(r.msg_code==200){
let checkPersonId = ''
r.data.forEach(item=>{
if(item.personId == row.personUnid){
checkPersonId = item.personId;
}
item.expand = false;
item.records.forEach(record=>{
record.picture_url = window._baseImgUrl +'picture/' +record.bodyPath+record.bodyPic
})
})
hitDetailDialogRef.value.initDialog({
regId:row.id,
data:r.data,
checkPersonId:checkPersonId
})
}
}
)
}
// 注册样本图
const regPictureList = ref([])
const hitPictureList = ref([])
const regPersonId = ref()
const redPersonObj = ref({})
const clickRow = function(row){
isLoading.value = true;
redPersonObj.value = row;
regPersonId.value = row.id;
regPictureList.value = []
hitPictureList.value = []
FeatureMatchingAccuracyApi.getMatchOne(row).then(
(r) => {
isLoading.value = false;
if(r.msg_code==200){
r.data.faceList.forEach(item=>{
item.picture_url = window._baseImgUrl +'picture/' +item.bodyPath+item.bodyPic
})
regPictureList.value = r.data.faceList
r.data.hitList.forEach(item=>{
item.picture_url = window._baseImgUrl +'picture/' +item.bodyPath+item.bodyPic
})
hitPictureList.value = r.data.hitList
}
}
)
}
// 识别结果
const identifyResultList = ref([])
const pageNum_identify = ref(1)
const pageSize_identify = ref(10)
const total_identify = ref(0)
const onPageNumChange_identify = function(num) {
pageNum_identify.value = num
clickPicture()
}
const onPageSizeChange_identify = function(current, size) {
pageNum_identify.value = 1
pageSize_identify.value = size
clickPicture()
}
const currentId = ref()
const clickPicture = function(row){
const rawData = toRaw(hitSearchObj)
// if(rawData.gate_id.length<1){
// ElMessage({
// message: '请选择监控点',
// type: 'warning'
// })
// return false;
// }
currentId.value = row.id
isLoading.value = true;
let data = filterEmptyValueInObject({
accountId: queryForm.account_id,
mallId: queryForm.plaza_id,
gateId: rawData.gate_id.toString(),
score: rawData.score,
direction: rawData.direction.toString(),
startTime: formatDate(rawData.date) + ' ' + formatTime(rawData.startTime),
endTime: formatDate(rawData.date) + ' ' + formatTime(rawData.endTime),
faceId: currentId.value,
page: pageNum_identify.value - 1,
pageSize: pageSize_identify.value,
regId:regPersonId.value
})
FeatureMatchingAccuracyApi.getSearchByPic(data).then(
(r) => {
isLoading.value = false;
if(r.msg_code==200){
console.log(r.data.records)
r.data.records.forEach(item=>{
item.checked = false;
item.featureNum = item.featureNum.toFixed(2)
item.picture_url = window._baseImgUrl +'picture/' +item.picture_path+item.picture_url
})
identifyResultList.value = r.data.records
total_identify.value = r.data.total
}
}
)
}
const searchPicture = function(){
if(!currentId.value){
ElMessage({
message: '请选择样本图',
type: 'warning'
})
return false;
}
clickPicture({id:currentId.value})
}
// 选中命中目标
const checkPicture = function(){
let checkList = []
identifyResultList.value.forEach(item=>{
if(item.checked==true){
checkList.push(item.id)
}
})
if(checkList.length<1){
ElMessage({
message: '请选择识别结果图片',
type: 'warning'
})
return false;
}
if(!regPersonId.value){
ElMessage({
message: '请选择注册人员',
type: 'warning'
})
return false;
}
isLoading.value = true;
FeatureMatchingAccuracyApi.addMatchHit({
id:regPersonId.value,
faceIds:checkList
}).then(
(r) => {
isLoading.value = false;
if(r.msg_code==200){
ElMessage({
message: r.msg_info,
type: 'success'
})
getTableData()
clickRow(redPersonObj.value)
searchPicture()
}
}
)
}
// 删除命中目标图片
const delHitPic = function(row){
FeatureMatchingAccuracyApi.delMatchHit({
regId:regPersonId.value,
faceId:row.id
}).then(
(r) => {
isLoading.value = false;
if(r.msg_code==200){
ElMessage({
message: r.msg_info,
type: 'success'
})
getTableData()
clickRow(redPersonObj.value)
searchPicture()
}
}
)
}
// 删除样本图片
const delSamplePic = function(row){
FeatureMatchingAccuracyApi.delMatchSample({
regId:regPersonId.value,
faceId:row.id
}).then(
(r) => {
isLoading.value = false;
if(r.msg_code==200){
ElMessage({
message: r.msg_info,
type: 'success'
})
getTableData()
clickRow(redPersonObj.value)
}
}
)
}
const getIndex = function(val) {
return (pageNum.value - 1) * pageSize.value + val + 1
}
const __main = function() {
contentHeight.value = window.innerHeight - 200
getAccountList()
}
__main()
return {
// sequence
isLoading,
pageNum,
pageSize,
total,
contentHeight,
accountList,
plazaList,
resultList,
addPersonConfigRef,
editPersonConfigRef,
hitSearchObj,
gateList,
activeKey: ref('1'),
regPictureList,
pageNum_identify,
pageSize_identify,
total_identify,
identifyResultList,
currentId,
hitPictureList,
hitDetailDialogRef,
// mapping
queryForm,
onAccountChange,
confirmSearch,
onPageNumChange,
onPageSizeChange,
addPersonFun,
getIndex,
editRow,
deleteRow,
onMallChange,
clickRow,
searchPicture,
clickPicture,
onPageNumChange_identify,
onPageSizeChange_identify,
checkPicture,
delHitPic,
hitDetail,
delSamplePic
}
}
}
</script>
<style lang="less" scoped>
.active{
border : 2px solid #40a9ff
}
.regBox{
height: 300px;
overflow-y: auto;
overflow-x: hidden;
}
.hitBox{
position: relative;
}
.delHitPic{
position: absolute;
right: 0;
top: 0;
color: #1890ff;
font-size: 28px;
}
.secondCondition{
margin-right: 5px !important;
}
.identifyResultTable{
overflow-y: auto;
}
.pictureBox{
position: relative;
cursor: pointer;
}
.checkBox{
position: absolute;
left: 1px;
top: 0;
}
.score{
position: absolute;
left: 50px;
top: 0;
color: red;
font-weight: 900;
}
.iconPic{
position: absolute;
right: 0px;
top: 0px;
color: #1890ff;
font-size: 28px;
}
.single-image{
width: 100%;
height: 200px;
}
.containter {
height: 100%;
}
.contentBox {
height: 100%;
}
.box {
height: 100%;
}
.boxRight {
border-left: 5px solid #ccc;
padding-left: 5px;
}
.btns {
text-align: right;
margin-top: 10px;
margin-right: 15px;
}
.btn {
margin-left: 10px;
}
</style>
import axiosInstance from "@/Request/PublicAxiosInstance"
import {filterEmptyValueInObject} from "@/PublicUtil/PublicUtil"
class FeatureMatchingAccuracy {
addMatch(data) {
return axiosInstance.request(
{
method: 'POST',
url: `/feature/match`,
data: data
}
)
}
getMatchOne(data) {
return axiosInstance.request(
{
method: 'GET',
url: `/feature/match/${data.id}`,
}
)
}
editMatch(data) {
return axiosInstance.request(
{
method: 'PUT',
url: `/feature/match`,
data: data
}
)
}
delMatch(data){
return axiosInstance.request(
{
method: 'DELETE',
url: `/feature/match/${data.id}`,
}
)
}
getMatch(data) {
return axiosInstance.request(
{
method: 'GET',
url: `/feature/match`,
params: data
}
)
}
getSearchByPic(data){
return axiosInstance.request(
{
method: 'GET',
url: `/feature/match/searchByPic`,
params: data
}
)
}
addMatchHit(data) {
return axiosInstance.request(
{
method: 'POST',
url: `/feature/match/hit`,
data: data
}
)
}
delMatchHit(data){
return axiosInstance.request(
{
method: 'DELETE',
url: `/feature/match/hit/${data.regId}/${data.faceId}`,
}
)
}
getMatchHitDetail(data){
return axiosInstance.request(
{
method: 'GET',
url: `/feature/match/hitDetail/${data.id}`,
params: data
}
)
}
getMatchHit(data){
return axiosInstance.request(
{
method: 'GET',
url: `/feature/match/${data.id}/${data.personUnid}`,
}
)
}
delMatchSample(data){
return axiosInstance.request(
{
method: 'DELETE',
url: `/feature/match/${data.regId}/${data.faceId}`,
}
)
}
}
const FeatureMatchingAccuracyApi = new FeatureMatchingAccuracy()
export default FeatureMatchingAccuracyApi
<template>
<a-modal
title="新增注册人员"
v-if="isVisible"
v-model:visible="isVisible"
width="1500px"
:dialog-style="{ top: '20px',height:'90%' }"
class="detail-modal"
>
<div style="height: 700px;">
<a-form :model="formObj" layout="inline" :label-col="{ style: { width: '100px' } }">
<a-form-item label="姓名:">
<a-input v-model:value="formObj.name" style="width: 500px">
</a-input>
</a-form-item>
<a-form-item label="描述:">
<a-input v-model:value="formObj.dec" style="width: 500px">
</a-input>
</a-form-item>
<a-form-item label="选择样本图:" class="pictures">
<a-tabs type="card" v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="本地上传">
<a-upload
:file-list="fileList"
:remove="handleRemove"
:before-upload="beforeUpload"
:multiple="false"
>
<a-button>
<upload-outlined></upload-outlined>
上传文件
</a-button>
</a-upload>
<a-button
type="primary"
:disabled="fileList.length === 0"
:loading="loading"
style="margin-top: 16px"
@click="handleUpload"
>
{{ loading ? '上传中' : '开始上传' }}
</a-button>
</a-tab-pane>
<a-tab-pane key="2" tab="抓拍记录图" class="resultPic">
<a-form-item label="监控点:" style="padding: 5px 0" class="secondCondition">
<a-select v-model:value="formObj.gate_id"
style="width: 270px"
mode="multiple"
:maxTagCount="1"
:options="gateList"
optionFilterProp="label"
show-search
>
</a-select>
</a-form-item>
<a-form-item label="方向:" style="padding: 5px 0" class="secondCondition">
<a-select v-model:value="formObj.direction"
mode="multiple"
:maxTagCount="1"
style="width: 200px">
<a-select-option :value="1"></a-select-option>
<a-select-option :value="-1"></a-select-option>
</a-select>
</a-form-item>
<a-form-item label="选择日期:" style="padding: 5px 0" class="secondCondition">
<a-date-picker v-model:value="formObj.date" :format="'YYYY-MM-DD'" style="width: 150px"/>
</a-form-item>
<a-form-item label="选择时间:" style="padding: 5px 0" class="secondCondition">
<a-time-picker v-model:value="formObj.startTime" style="width: 100px"/>
<a-time-picker v-model:value="formObj.endTime" style="width: 100px"/>
</a-form-item>
<a-form-item style="padding: 5px 0" class="secondCondition">
<a-button type="primary" @click="picSearch" :loading="isLoading" style="margin-right: 10px;">查询</a-button>
<!-- <a-button type="primary" @click="clickCheck" >选中</a-button> -->
</a-form-item>
<div class="resultContent" :style="{'height':contentHeight+'px'}">
<template v-for="person in dataList">
<div class="classBox" :class="person.expand?'expand':''">
<div>
<div class="boxInfo">
<span class="iconExpand" v-show="!person.expand"></span>
<span class="iconExpand" v-show="person.expand"></span>
<span class="expandWord" @click='expandChange(person)'>{{person.expand?'收起':'展开'}}</span>
<span style="padding-left: 10px;">人id:{{ ' ' + person.person_unid }} 图片数量:{{ person.perrsonList.length }}</span>
</div>
<el-row v-for="row in getPagedList(person.perrsonList, 8)">
<el-col :span="3" v-for="item in row">
<div style="margin: 0 5px" class="picBox">
<el-checkbox class="checkBox" v-model="item.checked"></el-checkbox>
<el-image :src="item.picture_url"
:fit="'fill'"
class="single-image" >
</el-image>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
</div>
<a-pagination
v-model:current="pageNum"
v-model:pageSize="pageSize"
:total="total"
:show-total="total => `共 ${total} 条`"
:pageSizeOptions="['10', '20', '40', '80']"
@change="onPageNumChange"
@showSizeChange="onPageSizeChange"
show-size-changer
show-quick-jumper
style="text-align:center"
/>
</a-tab-pane>
</a-tabs>
</a-form-item>
</a-form>
</div>
<template #footer>
<a-button @click="onCancel">返回</a-button>
<a-button @click="onConfirm" type="primary">确定</a-button>
</template>
</a-modal>
</template>
<script>
import moment from 'moment'
import { reactive, ref,toRaw } from "vue";
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
import {isArray } from '@/PublicUtil/Judgment'
import snapshotRecordApi from '@/views/SnapshotCluster/SnapshotRecord/SnapshotRecordApi'
import clusterResultApi from '@/views/SnapshotCluster/ClusterResult/ClusterResultApi'
import FeatureMatchingAccuracyApi from '@/views/FeatureMatchingAccuracy/FeatureMatchingAccuracyApi'
import {filterEmptyValueInObject, formatDate, formatTime, getPagedList} from '@/PublicUtil/PublicUtil'
import {ElMessage} from 'element-plus'
export default {
components: {
LoadingOutlined,
PlusOutlined,
},
setup(props,context) {
const isVisible = ref(false);
const pageNum = ref(1)
const pageSize = ref(10)
const total = ref()
const dataList = ref([])
// 暂时选中目标,但没提交
const checkList = ref([])
const formObj = reactive({
name: '',
dec: '',
gate_id: [],
direction: [1, -1],
date: moment(moment().format('YYYY-MM-DD'), 'YYYY-MM-DD'),
startTime: moment('00:00:00', 'HH:mm:ss'),
endTime: moment('23:59:59', 'HH:mm:ss'),
})
const isLoading = ref(false)
const accountId = ref('');
const mallId = ref('');
const gateList = ref([])
const getGateList = function() {
formObj.gate_id = []
gateList.value = []
snapshotRecordApi.getGateList(
{
account_id: accountId.value,
plaza_id: mallId.value,
type: 0,
}
).then(
(r) => {
if (isArray(r.data))
{
for (const item of r.data)
{
gateList.value.push(
{
value: item.id,
label: item.name,
}
)
}
picSearch()
}
}
)
}
const initDialog = function(parmas) {
accountId.value = parmas.account_id;
mallId.value = parmas.plaza_id;
formObj.name = '';
formObj.dec = '';
getGateList()
isVisible.value = true;
};
const onCancel = () => {
dataList.value = []
total.value = 0
isVisible.value = false;
};
const refreshParentTable = function(){
context.emit('refreshParentTable')
}
const onConfirm = function(){
let faceIds = []
dataList.value.forEach((itemPerson)=>{
itemPerson.perrsonList.forEach((item)=>{
if (item.checked) {
faceIds.push(item.id)
}
})
})
if(faceIds.length<1){
ElMessage({
message: `请选择图片`,
type: 'warning'
})
return false;
}
const data = filterEmptyValueInObject(
{
accountId: accountId.value,
mallId: mallId.value,
name: formObj.name,
description: formObj.dec,
faceIds:faceIds
}
)
FeatureMatchingAccuracyApi.addMatch(data).then(
(r) => {
if(r.msg_code==200){
ElMessage({
message: r.msg_info,
type: 'success'
})
refreshParentTable()
isVisible.value = false
}else{
ElMessage({
message: r.msg_info,
type: 'error'
})
return false;
}
}
)
}
const loading = ref(false);
const imageUrl = ref('');
const fileList = ref([]);
const beforeUpload = (file) => {
fileList.value = [file];
return false;
};
const handleRemove = (file) => {
const index = fileList.value.indexOf(file);
const newFileList = fileList.value.slice();
newFileList.splice(index, 1);
fileList.value = newFileList;
};
const handleUpload = function(){
const formData = new FormData();
fileList.value.forEach((file) => {
formData.append('files[]', file);
});
loading.value = true;
}
const sortDataList = function(list) {
list.sort(
(a, b) => {
return (b.perrsonList.length - a.perrsonList.length)
}
)
}
const picSearch = function(){
pageNum.value = 1
clickSearch()
}
const clickSearch = function(){
const rawData = toRaw(formObj)
const data = filterEmptyValueInObject(
{
account_id: accountId.value,
type: 0,
plaza_id: mallId.value,
gate_id: rawData.gate_id.toString(),
direction: rawData.direction.toString(),
picType: 2,
personType: '1, 0,',
startTime: formatDate(rawData.date) + ' ' + formatTime(rawData.startTime),
endTime: formatDate(rawData.date) + ' ' + formatTime(rawData.endTime),
page: pageNum.value - 1,
pageSize: pageSize.value,
}
)
clusterResultApi.getClusterResultList(data).then(
(r) => {
isLoading.value = false
sortDataList(r.data.persons)
r.data.persons.forEach((itemPerson)=>{
itemPerson.expand = false
itemPerson.perrsonList.forEach((item)=>{
item.checked = false;
if (item.picture_url) {
item.picture_url = window._baseImgUrl + item.picture_url
}
})
})
dataList.value = r.data.persons
total.value = r.data.pageNum
document.getElementsByClassName('resultContent')[0].scrollTop = 0
}
)
}
const onPageNumChange = function(num) {
pageNum.value = num
clickSearch()
}
const onPageSizeChange = function(current, size) {
pageNum.value = 1
pageSize.value = size
clickSearch()
}
const expandChange = function(data){
dataList.value.forEach(item=>{
if (data.person_unid == item.person_unid) {
item.expand = !item.expand
}
})
}
const clickCheck = function(){
dataList.value.forEach((itemPerson)=>{
itemPerson.perrsonList.forEach((item)=>{
if(item.checked){
checkList
}
})
})
}
return {
isVisible,
formObj,
onCancel,
onConfirm,
initDialog,
activeKey: ref('2'),
beforeUpload,
loading,
imageUrl,
fileList,
handleUpload,
handleRemove,
gateList,
isLoading,
clickSearch,
onPageNumChange,
onPageSizeChange,
total,
dataList,
pageNum,
pageSize,
getPagedList,
expandChange,
clickCheck,
refreshParentTable,
picSearch
};
},
};
</script>
<style lang="less" scoped>
.resultPic{
border: 1px solid #ddd;
padding: 0px 10px;
}
.pictures{
width: 100%;
margin-top: 10px;
}
.secondCondition{
// max-width: 300px;
display: inline-block;
}
.resultContent{
overflow: auto;
max-height: 540px;
min-height: 300px;
margin-bottom: 10px;
}
.boxInfo{
line-height: 28px;
margin-bottom: 5px;
}
.classBox{
margin: 7px 0;
border: solid 1px black;
height: 260px;
overflow-y: hidden;
}
.expand{
height: auto;
overflow: auto;
}
.expandWord{
color: #1890ff;
margin-right: 5px;
cursor: pointer;
float: right;
}
.iconExpand{
cursor: pointer;
float: right;
color: #1890ff;
margin-right: 20px;
}
.single-image{
width: 100%;
height: 220px;
}
.picBox{
position: relative;
}
.checkBox{
position: absolute;
left: 5px;
top: 0;
}
</style>
<template>
<a-modal
title="编辑注册人员"
v-model:visible="isVisible"
width="1500px"
:dialog-style="{ top: '20px',height:'90%' }"
class="detail-modal"
>
<div style="height: 700px;">
<a-form :model="formObj" layout="inline" :label-col="{ style: { width: '100px' } }">
<a-form-item label="姓名:">
<a-input v-model:value="formObj.name" style="width: 500px">
</a-input>
</a-form-item>
<a-form-item label="描述:">
<a-input v-model:value="formObj.dec" style="width: 500px">
</a-input>
</a-form-item>
<a-form-item label="选择样本图:" class="pictures">
<a-tabs type="card" v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="本地上传">
<a-upload
:file-list="fileList"
:remove="handleRemove"
:before-upload="beforeUpload"
:multiple="false"
>
<a-button>
<upload-outlined></upload-outlined>
上传文件
</a-button>
</a-upload>
<a-button
type="primary"
:disabled="fileList.length === 0"
:loading="loading"
style="margin-top: 16px"
@click="handleUpload"
>
{{ loading ? '上传中' : '开始上传' }}
</a-button>
</a-tab-pane>
<a-tab-pane key="2" tab="抓拍记录图" class="resultPic">
<a-form-item label="监控点:" style="padding: 5px 0" class="secondCondition">
<a-select v-model:value="formObj.gate_id"
style="width: 270px"
mode="multiple"
:maxTagCount="1"
:options="gateList"
optionFilterProp="label"
show-search
>
</a-select>
</a-form-item>
<a-form-item label="方向:" style="padding: 5px 0" class="secondCondition">
<a-select v-model:value="formObj.direction"
mode="multiple"
:maxTagCount="1"
style="width: 200px">
<a-select-option :value="1"></a-select-option>
<a-select-option :value="-1"></a-select-option>
</a-select>
</a-form-item>
<a-form-item label="选择日期:" style="padding: 5px 0" class="secondCondition">
<a-date-picker v-model:value="formObj.date" :format="'YYYY-MM-DD'" style="width: 150px"/>
</a-form-item>
<a-form-item label="选择时间:" style="padding: 5px 0" class="secondCondition">
<a-time-picker v-model:value="formObj.startTime" style="width: 100px"/>
<a-time-picker v-model:value="formObj.endTime" style="width: 100px"/>
</a-form-item>
<a-form-item style="padding: 5px 0" class="secondCondition">
<a-button type="primary" @click="picSearch" :loading="isLoading" style="margin-right: 10px;">查询</a-button>
<!-- <a-button type="primary" @click="clickCheck" >选中</a-button> -->
</a-form-item>
<div class="resultContent" :style="{'height':contentHeight+'px'}">
<template v-for="person in dataList">
<div class="classBox" :class="person.expand?'expand':''">
<div>
<div class="boxInfo">
<span class="iconExpand" v-show="!person.expand"></span>
<span class="iconExpand" v-show="person.expand"></span>
<span class="expandWord" @click='expandChange(person)'>{{person.expand?'收起':'展开'}}</span>
<span style="padding-left: 10px;">人id:{{ ' ' + person.person_unid }} 图片数量:{{ person.perrsonList.length }}</span>
</div>
<el-row v-for="row in getPagedList(person.perrsonList, 8)">
<el-col :span="3" v-for="item in row">
<div style="margin: 0 5px" class="picBox">
<el-checkbox class="checkBox" v-model="item.checked"></el-checkbox>
<el-image :src="item.picture_url"
:fit="'fill'"
class="single-image" >
</el-image>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
</div>
<a-pagination
v-model:current="pageNum"
v-model:pageSize="pageSize"
:total="total"
:show-total="total => `共 ${total} 条`"
:pageSizeOptions="['10', '20', '40', '80']"
@change="onPageNumChange"
@showSizeChange="onPageSizeChange"
show-size-changer
show-quick-jumper
style="text-align:center"
/>
</a-tab-pane>
</a-tabs>
</a-form-item>
</a-form>
</div>
<template #footer>
<a-button @click="onCancel">返回</a-button>
<a-button @click="onConfirm" type="primary">确定</a-button>
</template>
</a-modal>
</template>
<script>
import moment from 'moment'
import { reactive, ref,toRaw } from "vue";
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
import {isArray } from '@/PublicUtil/Judgment'
import snapshotRecordApi from '@/views/SnapshotCluster/SnapshotRecord/SnapshotRecordApi'
import clusterResultApi from '@/views/SnapshotCluster/ClusterResult/ClusterResultApi'
import FeatureMatchingAccuracyApi from '@/views/FeatureMatchingAccuracy/FeatureMatchingAccuracyApi'
import {filterEmptyValueInObject, formatDate, formatTime, getPagedList} from '@/PublicUtil/PublicUtil'
import {ElMessage} from 'element-plus'
export default {
components: {
LoadingOutlined,
PlusOutlined,
},
setup(props,context) {
const isVisible = ref(false);
const pageNum = ref(1)
const pageSize = ref(10)
const total = ref()
const dataList = ref([])
const formObj = reactive({
name: '',
dec: '',
gate_id: [],
direction: [1, -1],
date: moment(moment().format('YYYY-MM-DD'), 'YYYY-MM-DD'),
startTime: moment('00:00:00', 'HH:mm:ss'),
endTime: moment('23:59:59', 'HH:mm:ss'),
id:''
})
const isLoading = ref(false)
const accountId = ref('');
const mallId = ref('');
const gateList = ref([])
const checkGateList = ref([])
const getGateList = function() {
formObj.gate_id = []
gateList.value = []
snapshotRecordApi.getGateList(
{
account_id: accountId.value,
plaza_id: mallId.value,
type: 0,
}
).then(
(r) => {
if (isArray(r.data))
{
for (const item of r.data)
{
gateList.value.push(
{
value: item.id,
label: item.name,
}
)
}
picSearch()
}
}
)
}
const initDialog = function(parmas) {
accountId.value = parmas.accountId;
mallId.value = parmas.mallId;
formObj.name = parmas.name;
formObj.dec = parmas.description;
formObj.id = parmas.id;
// checkGateList.value = parmas.faceIds
getGateList()
isVisible.value = true;
};
const onCancel = () => {
isVisible.value = false;
};
const refreshParentTable = function(){
context.emit('refreshParentTable')
}
const onConfirm = function(){
let faceIds = []
dataList.value.forEach((itemPerson)=>{
itemPerson.perrsonList.forEach((item)=>{
if (item.checked) {
faceIds.push(item.id)
}
})
})
if(faceIds.length<1 && checkGateList.length<1){
ElMessage({
message: `请选择图片`,
type: 'warning'
})
return false;
}
const data = filterEmptyValueInObject(
{
// accountId: accountId.value,
// mallId: mallId.value,
name: formObj.name,
description: formObj.dec,
faceIds:faceIds.length<1?checkGateList.value:faceIds,
id:formObj.id
}
)
FeatureMatchingAccuracyApi.editMatch(data).then(
(r) => {
if(r.msg_code==200){
ElMessage({
message: r.msg_info,
type: 'success'
})
refreshParentTable()
isVisible.value = false
}else{
ElMessage({
message: r.msg_info,
type: 'error'
})
return false;
}
}
)
}
const loading = ref(false);
const imageUrl = ref('');
const fileList = ref([]);
const beforeUpload = (file) => {
fileList.value = [file];
return false;
};
const handleRemove = (file) => {
const index = fileList.value.indexOf(file);
const newFileList = fileList.value.slice();
newFileList.splice(index, 1);
fileList.value = newFileList;
};
const handleUpload = function(){
const formData = new FormData();
fileList.value.forEach((file) => {
formData.append('files[]', file);
});
loading.value = true;
}
const sortDataList = function(list) {
list.sort(
(a, b) => {
return (b.perrsonList.length - a.perrsonList.length)
}
)
}
const picSearch = function(){
pageNum.value = 1
clickSearch()
}
const clickSearch = function(){
const rawData = toRaw(formObj)
const data = filterEmptyValueInObject(
{
account_id: accountId.value,
type: 0,
plaza_id: mallId.value,
gate_id: rawData.gate_id.toString(),
direction: rawData.direction.toString(),
picType: 2,
personType: '1, 0,',
startTime: formatDate(rawData.date) + ' ' + formatTime(rawData.startTime),
endTime: formatDate(rawData.date) + ' ' + formatTime(rawData.endTime),
page: pageNum.value - 1,
pageSize: pageSize.value,
}
)
clusterResultApi.getClusterResultList(data).then(
(r) => {
isLoading.value = false
sortDataList(r.data.persons)
r.data.persons.forEach((itemPerson)=>{
itemPerson.expand = false
itemPerson.perrsonList.forEach((item)=>{
if(checkGateList.value.includes(item.id)){
item.checked = true;
}else{
item.checked = false;
}
if (item.picture_url) {
item.picture_url = window._baseImgUrl + item.picture_url
}
})
})
dataList.value = r.data.persons
total.value = r.data.pageNum
document.getElementsByClassName('resultContent')[0].scrollTop = 0
}
)
}
const onPageNumChange = function(num) {
pageNum.value = num
clickSearch()
}
const onPageSizeChange = function(current, size) {
pageNum.value = 1
pageSize.value = size
clickSearch()
}
const expandChange = function(data){
dataList.value.forEach(item=>{
if (data.person_unid == item.person_unid) {
item.expand = !item.expand
}
})
}
const clickCheck = function(){
console.log(dataList.value)
}
return {
isVisible,
formObj,
onCancel,
onConfirm,
initDialog,
activeKey: ref('2'),
beforeUpload,
loading,
imageUrl,
fileList,
handleUpload,
handleRemove,
gateList,
isLoading,
clickSearch,
onPageNumChange,
onPageSizeChange,
total,
dataList,
pageNum,
pageSize,
getPagedList,
expandChange,
clickCheck,
refreshParentTable,
picSearch
};
},
};
</script>
<style lang="less" scoped>
.resultPic{
border: 1px solid #ddd;
padding: 0px 10px;
}
.pictures{
width: 100%;
margin-top: 10px;
}
.secondCondition{
// max-width: 300px;
display: inline-block;
}
.resultContent{
overflow: auto;
max-height: 540px;
min-height: 300px;
margin-bottom: 10px;
}
.boxInfo{
line-height: 28px;
margin-bottom: 5px;
}
.classBox{
margin: 7px 0;
border: solid 1px black;
height: 260px;
overflow-y: hidden;
}
.expand{
height: auto;
overflow: auto;
}
.expandWord{
color: #1890ff;
margin-right: 5px;
cursor: pointer;
float: right;
}
.iconExpand{
cursor: pointer;
float: right;
color: #1890ff;
margin-right: 20px;
}
.single-image{
width: 100%;
height: 220px;
}
.picBox{
position: relative;
}
.checkBox{
position: absolute;
left: 5px;
top: 0;
}
</style>
<template>
<a-modal
title="命中详情"
v-if="isVisible"
v-model:visible="isVisible"
width="1500px"
:dialog-style="{ top: '20px',height:'90%' }"
class="detail-modal"
>
<div style="height: 700px;overflow-y: auto;">
<template v-for="person in hitPersonList">
<div class="classBox" :class="person.expand?'expand':''">
<div class="boxInfo">
<span class="iconExpand" v-show="!person.expand"></span>
<span class="iconExpand" v-show="person.expand"></span>
<span class="expandWord" @click='expandChange(person)'>{{person.expand?'收起':'展开'}}</span>
人id:{{ ' ' + person.personId }}
图片数量:{{ person.count }}
<el-radio class="checkBox" v-model="checked" :label="person.personId">以此为对比项</el-radio>
</div>
<el-row :gutter="10">
<el-col :span="3" v-for="item in person.records">
<div>
<el-image :src="item.picture_url"
:fit="'fill'"
class="single-image" >
</el-image>
</div>
</el-col>
</el-row>
</div>
</template>
</div>
<template #footer>
<a-button @click="onCancel">返回</a-button>
<a-button @click="onConfirm" type="primary">确定</a-button>
</template>
</a-modal>
</template>
<script>
import moment from 'moment'
import { reactive, ref,toRaw } from "vue";
import {isArray } from '@/PublicUtil/Judgment'
import {ElMessage} from 'element-plus'
import FeatureMatchingAccuracyApi from '@/views/FeatureMatchingAccuracy/FeatureMatchingAccuracyApi'
export default {
setup(props,context) {
const isVisible = ref(false);
const isLoading = ref(false)
const accountId = ref('');
const mallId = ref('');
const checked = ref('');
const hitPersonList = ref([])
const regId = ref()
const initDialog = function(parmas) {
regId.value = parmas.regId
hitPersonList.value = parmas.data
checked.value = parmas.checkPersonId
isVisible.value = true;
};
const expandChange = function(data){
hitPersonList.value.forEach(item=>{
if (data.personId == item.personId) {
item.expand = !item.expand
}
})
}
const refreshParentTable = function(){
context.emit('refreshParentTable')
}
const onCancel = () => {
isVisible.value = false;
};
const onConfirm = function(){
if(!checked.value){
ElMessage({
message: `请选择对比项`,
type: 'warning'
})
return false;
}
FeatureMatchingAccuracyApi.getMatchHit({
id:regId.value,
personUnid:checked.value
}).then(
(r) => {
if(r.msg_code==200){
ElMessage({
message: `${r.msg_info}`,
type: 'success'
})
refreshParentTable()
isVisible.value = false;
}else{
ElMessage({
message: `${r.msg_info}`,
type: 'error'
})
}
}
)
}
return {
isVisible,
onCancel,
onConfirm,
initDialog,
hitPersonList,
expandChange,
checked,
refreshParentTable
};
},
};
</script>
<style lang="less" scoped>
.single-image{
width: 100%;
height: 220px;
}
.boxInfo{
line-height: 28px;
margin-bottom: 10px;
}
.checkBox{
margin-left: 15px;
}
.classBox{
margin-bottom: 10px;
border: solid 1px black;
height: 265px;
overflow: hidden;
padding: 5px;
}
.expand{
height: auto;
overflow-y: auto;
}
.expandWord{
color: #1890ff;
margin-right: 5px;
cursor: pointer;
float: right;
}
.iconExpand{
cursor: pointer;
float: right;
color: #1890ff;
margin-right: 20px;
}
</style>
......@@ -103,7 +103,12 @@
<span style="padding: 0 5px">特征匹配</span>
</div>
</a-menu-item>
<a-menu-item :key="'/Main/FeatureMatchingAccuracy'">
<div class="flex-vertical-center">
<img :src="require('./Icons/2.svg')" style="height: auto;width:20px"/>
<span style="padding: 0 5px">特征匹配精度</span>
</div>
</a-menu-item>
</a-menu>
</el-aside>
<el-main>
......
......@@ -58,7 +58,7 @@ module.exports = {
devServer: {
proxy: {
'/': {
target: 'http://36.112.68.214:33333/btool/',
target: 'https://mall.keliuyun.com/btool/',
changeOrigin: true
}
}
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!