Commit 21c74456 by 李乾广

聚类结果功能优化,搜索弹出框新需求开发

1 parent e5f47175
......@@ -14,6 +14,10 @@ const menuRoute = [
component: () => import("@/views/dayAnalysisRerun/dayAnalysisRerun.vue"),
},
{
path: 'batchAnalysisRerun',
component: () => import("@/views/batchAnalysisRerun/batchAnalysisRerun.vue"),
},
{
path: 'DataRepair',
component: () => import("@/views/DataRepair/DataRepair.vue"),
},
......
......@@ -115,6 +115,12 @@
<span style="padding: 0 5px">全天分析重跑</span>
</div>
</a-menu-item>
<a-menu-item :key="'/Main/batchAnalysisRerun'">
<div class="flex-vertical-center">
<!-- <img :src="require('./Icons/1.svg')" style="height: auto;width:20px"/> -->
<span style="padding: 0 5px">接待批次重跑</span>
</div>
</a-menu-item>
<a-menu-item :key="'/Main/DataRepair'">
<div class="flex-vertical-center">
<!-- <img :src="require('./Icons/2.svg')" style="height: auto;width:20px"/> -->
......
......@@ -90,6 +90,19 @@
<a-select-option :value="-1">未知</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="接待类型:" style="padding: 5px 0">
<a-select v-model:value="queryForm.receptionType" style="width: 240px">
<a-select-option value="">全部</a-select-option>
<a-select-option :value="1">接待</a-select-option>
<a-select-option :value="2">未接待</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="排序类型:" style="padding: 5px 0">
<a-select v-model:value="queryForm.sortType" style="width: 240px">
<a-select-option value="">按聚类数量</a-select-option>
<a-select-option :value="1">按时间</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="人ID:" style="padding: 5px 0">
<a-input v-model:value="queryForm.personUnid" style="width: 240px"/>
</a-form-item>
......@@ -136,6 +149,14 @@
</a-popconfirm> -->
<el-button type="text" :disabled="!isMultipleOperation" @click="deletePersonRecord(person)">剔除</el-button>
<a-popconfirm
title="是否添加至店员库"
ok-text="是"
cancel-text="否"
@confirm="addShopkeeper(person)"
>
<el-button type="text" :disabled="!isMultipleOperation">添加店员库</el-button>
</a-popconfirm>
<a-popconfirm
title="将彻底删除,您确认吗?"
ok-text="是"
cancel-text="否"
......@@ -143,6 +164,7 @@
>
<el-button type="text" :disabled="!isMultipleOperation">删除</el-button>
</a-popconfirm>
</a-form-item>
</a-form>
......@@ -293,8 +315,10 @@ export default {
endTime: '23:59:59',
minPic: 0,
personUnid:'',
maxPic: 100,
maxPic: 1000,
childAdult:[],
receptionType:'',
sortType:'',
}
)
const searchCondition = ref({})
......@@ -311,7 +335,7 @@ export default {
queryForm.startTime = searchCondition.value.startTime;
queryForm.endTime = searchCondition.value.endTime;
queryForm.minPic = searchCondition.value.minPic||0;
queryForm.maxPic = searchCondition.value.maxPic||100;
queryForm.maxPic = searchCondition.value.maxPic||1000;
}
// function
const onPageNumChange = function(num) {
......@@ -510,6 +534,8 @@ export default {
page: pageNum.value - 1,
pageSize: pageSize.value,
childAdult: rawData.childAdult?rawData.childAdult.toString():'',
receptionType: rawData.receptionType?rawData.receptionType:0,
sortType: rawData.sortType?rawData.sortType:0,
}
)
const storageData = filterEmptyValueInObject(
......@@ -778,19 +804,77 @@ export default {
}
)
}
// 添加店员库
const addShopkeeper = () => {
console.log('addShopkeeper', selectedPersonList)
if (selectedPersonList.value.length < 1) {
ElMessage({
message: `至少选择一条数据`,
type: 'error'
})
return
}
const strIdList = selectedPersonList.value.map(item => item.id).join(',')
const rawData = toRaw(queryForm)
const params = {
unids: strIdList,
mallId:currobj.value.mall_id,
countdate:formatDate(rawData.date),
}
clusterResultApi.addDataToShopkeeper(params).then(
(r) => {
if(r.msg_code==200){
ElMessage({
message: `添加成功`,
type: 'success'
})
selectedPersonList.value = []
// 刷新列表
handleRefresh()
} else {
ElMessage({
message: `添加失败`,
type: 'error'
})
}
}
)
}
// 移动人员
const personGroupMoverRef = ref();
const movePersonRecord = () => {
console.log('movePersonRecord')
if (selectedPersonList.value.length < 1) {
let selectedList = []
// 添加人员组
dataList.value.forEach(item=>{
if (item.checked && item.checked == true&&item.perrsonList.length > 0) {
console.log(111,item.perrsonList)
selectedList = selectedList.concat(item.perrsonList)
}
})
if (selectedList.length==0&&selectedPersonList.value.length < 1) {
ElMessage({
message: `至少选择一条数据`,
type: 'error'
})
return
}
personGroupMoverRef.value.initDialog(selectedPersonList.value);
// 添加单个图片
if (selectedPersonList.value.length > 0) {
for(let j=0;j<selectedPersonList.value.length;j++) {
let isAdd = true
for(let i=0;i<selectedList.length;i++) {
if(selectedList[i].unid==selectedPersonList.value[j].unid){
isAdd = false;
break;
}
}
if(isAdd) {
selectedList.push(selectedPersonList.value[j])
}
}
}
personGroupMoverRef.value.initDialog(selectedList);
}
// 查询组
const personGroupDialogRef = ref();
......@@ -807,7 +891,8 @@ export default {
plaza_id:currobj.value.mall_id
} */
// singleImgComparisonRef.value.initDialog(parmas);
const rawData = toRaw(queryForm)
data.plaza_id = rawData.plaza_id
personGroupDialogRef.value.initDialog(data);
}
......@@ -1060,6 +1145,7 @@ export default {
handleMutipleOperation,
mutipleOperationText,
deleteRealPersonRecord,
addShopkeeper,
}
}
}
......
......@@ -123,6 +123,16 @@ class ClusterResultApi {
}
)
}
// 添加店员库
addDataToShopkeeper(data) {
return axiosInstance.request(
{
method: 'POST',
url: `/faceRecognitions/addStaff`,
data: data
}
)
}
// 剔除或移动
updateRecognition(data) {
return axiosInstance.request(
......@@ -155,6 +165,37 @@ class ClusterResultApi {
}
)
}
// 搜索左侧图片列表
getAllfacePic(data) {
return axiosInstance.request(
{
method: 'GET',
url: `/faceRecognitions/facePic`,
params: filterEmptyValueInObject(
data
)
}
)
}
// 搜索左侧图片点击20条记录列表
getAllFaceQuerPic(data) {
return axiosInstance.request(
// {
// method: 'GET',
// url: `/faceRecognitions/querFaceRecogniton`,
// params: filterEmptyValueInObject(
// data
// )
// }
{
method: 'POST',
url: `/faceRecognitions/querFaceRecogniton`,
data: data
}
)
}
}
const clusterResultApi = new ClusterResultApi()
......
......@@ -8,6 +8,7 @@
centered
destroyOnClose
class="detail-modal"
@cancel="onCancel"
>
<div class="person-group">
<div class="seart-part">
......@@ -22,27 +23,27 @@
<a-form-item style="padding: 5px 0">
<a-button type="primary" @click="clickSearch" :loading="isLoading">查询</a-button>
<a-button @click="handleAddGroup()" :loading="addGroupLoading" style="margin-left: 10px">加入选中图片</a-button>
<a-alert style="display: inline-block;margin-left: 20px" message="提示:单击选择图片,再次单击,取消选择。双击预览图片。" type="info" />
<a-alert style="display: inline-block;margin-left: 20px" message="提示:单击选择图片,再次单击,取消选择。双击预览图片。Ctrl+单击图片可搜索该图片后最近20条抓拍记录。" type="info" />
</a-form-item>
</a-form>
</div>
<div class="content-part">
<div class="left-part">
<el-row>
<el-row style="height: 270px;overflow: auto;display: block;">
<el-col :span="4" v-for="item in personList" :key="item.id">
<div
style="margin: 0 5px"
:class="item.id === currentPerson.id ? 'actived' : ''"
@click="handleClick(item)"
:class="currentPersonIds.indexOf(item.id) != -1 ? 'actived' : ''"
@click="handleClick(item,true)"
@dblclick="handlePreview(item.picture_url)"
>
<el-image :src="item.picture_url"
<!-- <el-image :src="item.picture_url" -->
<el-image :src="formatImgUrl(item.picture_url)"
:fit="'fill'"
class="single-image">
</el-image>
<!--<div>时间:{{ item.counttime }}</div>-->
<!--<div>人员类型:{{ item.person_type==1?'店员':(item.person_type==0?'顾客':'未知') }}({{ item.childAdult==1?'成人':(item.childAdult==0?'儿童':'未知') }})</div>-->
<!--<div>性别:{{ formatGender(item.gender) }}({{item.age}})</div>-->
......@@ -51,12 +52,23 @@
</div>
</el-col>
</el-row>
<div style="margin-top: 10px;">指定图片时间后最近抓拍记录</div >
<el-row style="height: 270px;overflow: auto;display: block;">
<el-col :span="4" v-for="item in personQuerList" :key="item.id">
<div style="margin: 0 5px" :class="currentPersonIds.indexOf(item.id) != -1 ? 'actived' : ''" @click="handleClick(item,false)" @dblclick="handlePreview(item.picture_url)">
<el-image :src="formatImgUrl(item.picture_url)" :fit="'fill'" class="single-image"></el-image>
</div>
</el-col>
</el-row>
</div>
<div class="right-part">
<template v-if="groupList.length > 0">
<div class="classBox" v-for="row in groupList" :key="row.person_unid">
<div style="text-align: right;margin-bottom: 10px;">
<a-button @click="handleAddGroup(row.personList)" :loading="addGroupLoading" style="margin-left: 10px">加入此分组内图片</a-button>
<div style="display: flex;align-items: center;justify-content: space-between;position: relative;top:-5px;">
<span>{{row.person_unid}}</span>
<div>
<a-button @click="handleAddGroup(row.personList)" :loading="addGroupLoading">加入此分组内图片</a-button>
</div>
</div>
<el-row>
<el-col class="itemBox" :span="3" v-for="item in row.personList" :key="item.id">
......@@ -112,9 +124,11 @@ export default {
setup(props, { emit }) {
const isVisible = ref(false);
const personList = ref([])
const personQuerList = ref([])
// 右侧分组
const groupList = ref([])
const currentPerson = ref({})
const currentPerson = ref([])
const currentPersonIds = ref('')
// 表单
function getInitialFormData() {
return {
......@@ -125,6 +139,7 @@ export default {
}
const formData = ref(getInitialFormData())
const personUnid = ref('')
const plazaId = ref('')
const initDialog = (data) => {
console.log('initDialog', data)
......@@ -132,14 +147,16 @@ export default {
// 初始化数据
groupList.value = []
personList.value = []
personQuerList.value = []
currentPersonIds.value = ''
currentPerson.value = []
isLoading.value = false
isVisible.value = true;
// 获取组数据
personList.value = data.perrsonList || []
personUnid.value = data.person_unid
plazaId.value = data.plaza_id
// 搜索搜索条件默认值
formData.value.startTime = '00:00:00'
formData.value.endTime = '23:59:59'
......@@ -148,14 +165,58 @@ export default {
} else {
formData.value.date = moment().format('YYYY-MM-DD')
}
getAllLeftPic(data)
document.addEventListener('keydown', keydownClick);
document.addEventListener('keyup', keyupClick);
};
// 查询左侧聚类结果
const getAllLeftPic = () => {
const parmas = {
person_unid: personUnid.value,
plaza_id: plazaId.value,
endTime: formData.value.date + ' 23:59:59',
startTime: formData.value.date + ' 00:00:00',
countdate: formData.value.date,
}
clusterResultApi.getAllfacePic(parmas).then((r) => {
if(r.msg_code === 200) {
personList.value = r.data || []
} else {
ElMessage({
message: `查询失败`,
type: 'error'
})
}
}).finally(() => {
})
};
// 查询指定抓拍记录最近的20个抓拍记录
const getAllLeftQuerPic = (unids) => {
const parmas = {
unids: [unids],
mallId: plazaId.value,
countdate: formData.value.date + ' 00:00:00',
}
clusterResultApi.getAllFaceQuerPic(parmas).then((r) => {
if(r.msg_code === 200&&r.data&&r.data[0]&&r.data[0].perrsonList) {
personQuerList.value = personQuerList.value.concat(r.data[0].perrsonList)
} else {
ElMessage({
message: `查询失败`,
type: 'error'
})
}
}).finally(() => {
})
};
const formatImgUrl = (url) => {
return window._baseImgUrl + url
}
const selectedImgList = ref([])
const handleSelectImg = (data) => {
console.log('handleSelectImg', data)
// console.log('handleSelectImg', data)
const isExist = selectedImgList.value.some(item => item.id === data.id)
if (isExist) {
// 删除
......@@ -178,15 +239,17 @@ export default {
personUnid: personUnid.value,
countdate:formData.value.date,
}
addGroupLoading.value = true
clusterResultApi.updateRecognition(params).then((r) => {
console.log('r', r)
if(r.msg_code === 200) {
ElMessage({
message: `添加成功`,
type: 'success'
})
for(let i=0;i<targetDataList.length;i++) {
targetDataList[i].person_unid = personList.value[0].person_unid
}
personList.value = personList.value.concat(targetDataList)
selectedImgList.value = []
clickSearch()
......@@ -207,9 +270,43 @@ export default {
}
}
const handleClick = (data) => {
console.log('handleClick', data)
currentPerson.value = data
const handleClick = (data,type) => {
// console.log('handleClick', data.id)
if(keydownCtrlKey.value&&type) {
console.log('搜索20条')
getAllLeftQuerPic(data.unid)
} else {
let currentPersonArr = currentPersonIds.value?currentPersonIds.value.split(','):[];
if(currentPersonIds.value.indexOf(data.id)!=-1) {
for(let i=0;i<currentPersonArr.length;i++) {
if(currentPersonArr[i]==data.id){
currentPerson.value.splice(i,1)
currentPersonArr.splice(i,1)
break;
}
}
} else {
currentPerson.value.push(data)
currentPersonArr.push(data.id)
}
currentPersonIds.value = currentPersonArr.join(',')
// 计算开始结束时间
if(currentPerson.value.length>0) {
let currentDay = currentPerson.value[0].counttime.split(' ')[0] + ' '
let startTime = ''
let endTime = ''
for(let j=0;j<currentPerson.value.length;j++) {
let oneTime = currentPerson.value[j].counttime.split(' ')[1]
startTime = new Date(currentPerson.value[j].counttime).getHours()<3?'03:00:00':(startTime==''||oneTime<startTime)?oneTime:startTime
endTime = new Date(currentPerson.value[j].counttime).getHours()>20?'20:59:59':(endTime==''||oneTime>endTime)?oneTime:endTime
}
formData.value.startTime = moment(currentDay+startTime).subtract('hours',3).format('HH:mm:ss')
formData.value.endTime = moment(currentDay+endTime).add('hours',3).format('HH:mm:ss')
} else {
formData.value.startTime = '00:00:00'
formData.value.endTime = '23:59:59'
}
}
}
const handlePreview = (url) => {
console.log('handlePreview', url)
......@@ -239,8 +336,20 @@ export default {
}
}
}
const keydownCtrlKey = ref(false)
const keydownClick = (event) => {
if (event.ctrlKey) {
keydownCtrlKey.value = true
} else {
keydownCtrlKey.value = false
}
};
const keyupClick = () => {
keydownCtrlKey.value = false
};
const onCancel = () => {
document.removeEventListener('keydown',keydownClick)
document.removeEventListener('keyup',keyupClick)
isVisible.value = false;
groupList.value = []
};
......@@ -248,7 +357,7 @@ export default {
const isLoading = ref(false)
const clickSearch = () => {
const parmas = {
person_unid: currentPerson.value.person_unid,
person_unid: personUnid.value,
pic_type: 2,
endTime: formData.value.date + ' ' + formData.value.endTime,
......@@ -259,14 +368,12 @@ export default {
endTime: '2024-04-10 23:59:59',
countdate: '2024-04-10', */
pic_id: currentPerson.value.id,
pic_id: currentPersonIds.value,
ip: window._baseImgUrl,
plaza_id: currentPerson.value.mall_id
plaza_id: plazaId.value,
}
isLoading.value = true
featureApi.getAllPersonContrastList(parmas).then((r) => {
console.log('featureApi', r)
if(r.msg_code === 200) {
groupList.value = r.data
} else {
......@@ -280,10 +387,14 @@ export default {
})
};
return {
keydownClick,
isVisible,
formData,
personList,
personQuerList,
groupList,
handleClick,
onCancel,
......@@ -291,6 +402,7 @@ export default {
clickSearch,
isLoading,
currentPerson,
currentPersonIds,
formatImgUrl,
formatDirection,
selectedImgList,
......@@ -299,6 +411,8 @@ export default {
handleAddGroup,
addGroupLoading,
handlePreview,
getAllLeftPic,
getAllLeftQuerPic,
};
},
};
......@@ -323,9 +437,9 @@ export default {
display: flex;
}
.left-part {
width: 400px;
width: 500px;
height: 560px;
overflow: auto;
// overflow: auto;
//border: solid 1px black;
margin-right: 10px;
}
......
......@@ -142,6 +142,7 @@
:style="{'height':imgHeight+'px'}"
>
</el-image>
<div @click="personUnidClick(item.person_unid)">人id: {{ item.person_unid }}</div>
<div>时间:{{ item.counttime }}</div>
<div>人员类型:{{ personTypeStr(item.person_type)}}({{ item.childAdult==1?'成人':(item.childAdult==0?'儿童':'未知') }})</div>
<div>
......@@ -527,6 +528,10 @@ export default {
const personTypeStr = function(val){
return (personTypeList.value.filter(v => v.value == val)[0] || {label:'--'}).label
}
// 点击人id,将人id复制到剪切板
const personUnidClick = function(val){
// return (personTypeList.value.filter(v => v.value == val)[0] || {label:'--'}).label
}
const formatGender = function(number){
switch (number)
{
......@@ -1341,6 +1346,7 @@ export default {
imgModelRef,
singleComparativeFun,
singleImgComparisonRef,
personUnidClick,
hairStyleStr,
hatStr,
glassesStr,
......
<template>
<a-form class="data-return-form" :model="queryForm" layout="inline" :label-col="{ style: { width: '70px' } }">
<a-form-item label="集团:">
<a-select v-model:value="queryForm.account_id" style="width: 280px" mode="multiple" :maxTagCount="1" :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: 280px" mode="multiple" :maxTagCount="1" :options="plazaList" optionFilterProp="label" show-search>
</a-select>
</a-form-item>
<a-form-item label="开始日期:">
<a-date-picker v-model:value="queryForm.startDate" />
</a-form-item>
<a-form-item label="结束日期:">
<a-date-picker v-model:value="queryForm.endDate" />
</a-form-item>
<a-form-item>
<a-button type="primary" @click="confirmSearch">开始</a-button>
</a-form-item>
</a-form>
<!--新的日志-->
<div class="card-container">
<a-card title="接待批次重跑结果" style="width: 600px">
<el-row v-for="(item,index) in msgLogList" :key="index" style="padding: 2px 0;">
<el-col :span="8">
{{item.time}}
</el-col>
<el-col :span="16">
接待批次重跑任务已提交,正在重跑中...
</el-col>
</el-row>
</a-card>
</div>
</template>
<script>
import { reactive, ref, toRaw } from 'vue'
import moment from 'moment'
import { ElMessage } from 'element-plus'
import snapshotRecordApi from '@/views/SnapshotCluster/SnapshotRecord/SnapshotRecordApi'
import { isArray } from '@/PublicUtil/Judgment'
import { formatDate } from '@/PublicUtil/PublicUtil'
import batchAnalysisRerunApi from '@/views/batchAnalysisRerun/batchAnalysisRerunApi'
export default {
components: {
VNodes: (_, {attrs}) => {
return attrs.vnodes
},
},
setup() {
const accountList = ref([])
const plazaList = ref([])
const queryForm = reactive({
account_id: [],
plaza_id: [],
startDate: moment(moment().format('YYYY-MM-DD'), 'YYYY-MM-DD'),
endDate: moment(moment().format('YYYY-MM-DD'), 'YYYY-MM-DD'),
})
const onAccountChange = function() {
getPlazaList()
}
const getPlazaList = function() {
queryForm.plaza_id = []
plazaList.value = []
snapshotRecordApi.getPlazaList({
account_id: queryForm.account_id.toString()
}).then(
(r) => {
if (isArray(r)) {
for (const item of r) {
plazaList.value.push({
value: item.id,
label: item.name,
})
}
}
}
)
}
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,
})
}
}
}
)
}
// 新需求:数据重跑
const msgLogList = ref([])
const confirmSearch = function() {
if (queryForm.account_id.length < 1) {
ElMessage({
message: `至少选择一个集团`,
type: 'error'
})
return
}
if (queryForm.plaza_id.length < 1) {
ElMessage({
message: `至少选择一个门店`,
type: 'error'
})
return
}
msgLogList.value = []
const rawData = toRaw(queryForm)
const params = {
mallIds:queryForm.plaza_id.join(','),
startDate: formatDate(rawData.startDate),
endDate: formatDate(rawData.endDate),
}
batchAnalysisRerunApi.getResult(params).then(
(r) => {
console.log('getResult', r)
if (r.msg_code==200) {
msgLogList.value.push({time:moment().format('YYYY-MM-DD HH:mm:ss')})
}
}
)
}
const __main = function() {
getAccountList()
}
__main()
return {
accountList,
plazaList,
queryForm,
onAccountChange,
confirmSearch,
msgLogList,
}
}
}
</script>
<style lang="less" scoped>
.result-wrapper-2 {
width: 50%;
min-width: 600px;
border-radius: 4px;
margin: 20px auto 0;
position: relative;
overflow: auto;
}
.card-container {
display: flex;
justify-content: center;
/deep/.ant-card-body {
height: 370px;
}
}
.data-return-form {
/deep/.ant-form-item {
margin-bottom: 10px;
}
}
</style>
\ No newline at end of file
import axiosInstance from "@/Request/PublicAxiosInstance"
class BatchAnalysisRerunApi {
getResult(data) {
return axiosInstance.request(
{
method: 'GET',
url: `/reid/reception/reCall`,
params: data
}
)
}
}
const batchAnalysisRerunApi = new BatchAnalysisRerunApi()
export default batchAnalysisRerunApi
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!