rhb-server/mes-ui/rhb-app/packageQuery/pages/query.vue

643 lines
16 KiB
Vue
Raw Normal View History

2025-10-20 11:14:41 +08:00
<template>
<view class="container">
<view class="header">
<view class="search-box">
<view class="input-button-container">
<view class="search-input-wrapper">
<u-input v-model="searchKeyword" placeholder="搜索户主姓名或身份证号" prefix-icon="search"
@confirm="handleSearch" :disabled="isSerach" clearable />
</view>
<view class="scan-button" @click="handleScan">
<uni-icons type="scan" size="28" color="#28A0F8"></uni-icons>
</view>
<view class="search-button-wrapper">
<u-button @click="handleSearch" :disabled="isSerach" type="error" size="medium">
<u-icon name="search" size="24" />
搜索
</u-button>
</view>
</view>
<view class="area-select-row">
<!-- / -->
<view class="select-item">
<uni-easyinput prefixIcon="search" v-model="selectedTown" placeholder="请选择镇/街"
@clear="clearTown" @iconClick="showTown" clearable />
</view>
<!-- /社区 -->
<view class="select-item">
<uni-easyinput prefixIcon="search" v-model="selectedVillage" @clear="clearVillage"
placeholder="请选择村/社区" clearable @iconClick="showVillage()" />
</view>
<!-- /网格 -->
<view class="select-item">
<uni-easyinput prefixIcon="search" v-model="selectedGroup" placeholder="请选择组/网格" clearable
@iconClick="showGroup()" />
</view>
<!-- 镇选择器 -->
<u-picker :show="showTownPicker" mode="region" :columns="[townList]" keyName="label"
@confirm="onTownConfirm" @close="showTownPicker = false" />
<!-- 村选择器 -->
<u-picker :show="showVillagePicker" mode="region" :columns="[villageList]" keyName="label"
@confirm="onVillageConfirm" @close="showVillagePicker = false" />
<!-- 组选择器 -->
<u-picker :show="showGroupPicker" mode="region" :columns="[groupList]" keyName="label"
@confirm="onGroupConfirm" @close="showGroupPicker = false" />
</view>
<view class="area-select-row">
<view class="select-item1">
<uni-easyinput prefixIcon="search" v-model="selectDoorNum"
placeholder="请输入门牌号,若要未填写门牌号数据,请输入无" clearable />
</view>
</view>
</view>
</view>
<view class="content">
<view class="filter-tabs">
<view class="tab-item" :class="{ active: currentStatus === '' }" @click="filterByStatus('')">
{{getTitel('')}}
</view>
<view class="tab-item" :class="{ active: currentStatus === '1' }" @click="filterByStatus('1')">
{{getTitel('1')}}
</view>
<view class="tab-item" :class="{ active: currentStatus === '0' }" @click="filterByStatus('0')">
{{getTitel('0')}}
</view>
</view>
<view class="list-container">
<mescroll-body ref="mescrollRef" :up="upOption" @init="mescrollInit" @up="upCallback" :down="downOption"
@down="downCallback" :height="mescrollHeight">
<view v-if="loading && householdList.length === 0" class="loading">
<u-loading-icon mode="spinner" size="40"></u-loading-icon>
<text>加载中...</text>
</view>
<view v-else-if="householdList.length === 0" class="empty">
<u-icon name="info-circle" size="80" color="#ccc"></u-icon>
<text>暂无数据</text>
</view>
<view v-else class="household-list">
<view class="household-item" v-for="item in householdList" :key="item.id"
@click="viewDetail(item)">
<view class="item-header">
<text class="householder-name">{{ getMsg(item.householderName) }}</text>
<view class="status-tag" :class="item.submitStatus === 1 ? 'submitted' : 'draft'">
{{ item.submitStatus === 1 ? '已提交' : '暂存' }}
</view>
</view>
<view class="item-info">
<text class="info-text">{{ '身份证号:'+getMsg(item.householderIdNumber) }}</text>
<text class="info-text">{{ '联系电话:'+getMsg(item.householderPhone) }}</text>
<text class="info-text">{{'户籍人数:'+getMsg(item.householdNum) }}</text>
<text class="info-text">{{'地址:'}}{{ getMsg(item.town) }}{{ getMsg(item.village) }}{{getMsg(item.groupName)}}{{ getMsg(item.doorNumber) }}</text>
<text class="info-text">{{ '入户员:'+ getMsg(item.submitterName) }}</text>
</view>
<view class="item-footer">
<text class="time-text">{{ '入户时间:'+formatTime(item.updateTime) }}</text>
</view>
</view>
</view>
</mescroll-body>
</view>
</view>
</view>
</template>
<script>
import MescrollBody from 'mescroll-uni/mescroll-body.vue'
import MescrollMixin from 'mescroll-uni/mescroll-mixins.js'
import {
householdApi,areaApi
} from '../../utils/api.js'
import CryptoJS from "crypto-js";
let Qrcode = require('../../utils/reqrcode.js') //引用二维码(源码)
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
qrCodeRes: '',
searchKeyword: '',
total: '',
isSerach: false,
type: 'select',
secretKey: "m+=sad_sgAlo+1sag",
currentStatus: '',
householdList: [],
// 选择器弹窗控制
showTownPicker: false,
showVillagePicker: false,
showGroupPicker: false,
// 选中的值
selectedTown: '',
selectedVillage: '',
selectedGroup: '',
selectDoorNum: '',
// 下拉数据
townList: [],
villageList: [],
groupList: [],
// 三级原始数据
townVillageGroup: [],
loading: false,
upOption: {
use: true,
auto: true,
page: {
size: 5
},
noMoreSize: 5,
empty: {
tip: '暂无数据'
}
},
downOption: {
use: true
},
mescrollHeight: 'auto',
userInfo: {},
query: {}
}
},
async onLoad() {
this.loadUserInfo()
await this.listAreaNode();
},
methods: {
getMsg(value){
if(value){
return value
}else{
return ''
}
},
async listAreaNode() {
var areaDate = await areaApi.getAreaList();
this.townVillageGroup = this.transformTree(areaDate);
this.townList = this.townVillageGroup
},
transformTree(nodes) {
return nodes.map(node => {
// 创建基础对象只包含label和value
const newNode = {
"label": node.name,
"value": node.name
};
// 如果存在非空children则递归处理
if (node.children && node.children.length > 0) {
newNode.children = this.transformTree(node.children);
}
return newNode;
});
},
handleScan() {
// #ifdef APP-PLUS || MP-WEIXIN
uni.scanCode({
success: (res) => {
var msg = res.result
const bytes = CryptoJS.AES.decrypt(msg, this.secretKey);
this.searchKeyword = bytes.toString(CryptoJS.enc.Utf8);
this.mescroll.resetUpScroll()
},
fail: () => {
uni.showToast({
title: '扫码失败',
icon: 'none'
})
}
})
// #endif
// #ifdef H5
this.scanCodeH5()
// #endif
},
//h5识别条码和二维码
scanCodeH5() {
uni.chooseImage({
count: 1,
success: imgRes => {
Qrcode.qrcode.decode(this.getObjectURL(imgRes.tempFiles[0]))
Qrcode.qrcode.callback = (codeRes) => {
if (codeRes.indexOf('error') >= 0) {
// 二维码识别失败
uni.showToast({
title: '扫码失败',
icon: 'none'
})
} else {
const bytes = CryptoJS.AES.decrypt(codeRes, this.secretKey);
this.searchKeyword = bytes.toString(CryptoJS.enc.Utf8);
this.mescroll.resetUpScroll()
}
}
}
})
},
getTitel(current) {
if (current === '') {
if (this.currentStatus != '' || this.total == '' || this.total == 0) {
return '全部'
} else {
return '全部(' + this.total + ')'
}
} else if (current === '1') {
if (this.currentStatus != '1' || this.total == '' || this.total == 0) {
return '已提交'
} else {
return '已提交(' + this.total + ')'
}
} else if (current === '0') {
if (this.currentStatus != '0' || this.total == '' || this.total == 0) {
return '暂存'
} else {
return '暂存(' + this.total + ')'
}
}
},
// 获取文件地址函数
getObjectURL(file) {
var url = null
if (window.createObjectURL !== undefined) { // basic
url = window.createObjectURL(file)
} else if (window.URL !== undefined) { // mozilla(firefox)
url = window.URL.createObjectURL(file)
} else if (window.webkitURL !== undefined) { // webkit or chrome
url = window.webkitURL.createObjectURL(file)
}
return url
},
// 解码,输出:中文
decodeStr(str) {
var out, i, len, c;
var char2, char3;
out = "";
len = str.length;
i = 0;
while (i < len) {
c = str.charCodeAt(i++);
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
out += str.charAt(i - 1);
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
char3 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
},
mescrollInit(mescroll) {
this.mescroll = mescroll
},
loadUserInfo() {
const userInfo = uni.getStorageSync('userInfo')
if (userInfo) {
this.userInfo = userInfo
} else {
uni.reLaunch({
url: '/packageUser/pages/login'
})
}
},
handleSearch() {
this.mescroll.resetUpScroll()
},
filterByStatus(status) {
this.currentStatus = status
this.mescroll.resetUpScroll()
},
clearTown() {
this.selectedGroup = '';
this.selectedVillage = '';
},
clearVillage() {
this.selectedGroup = '';
},
// 镇选择
onTownConfirm(e) {
const town = e.value[0]
this.selectedTown = town.label
// 找到对应村
this.villageList = town.children || []
this.selectedVillage = ''
this.groupList = []
this.selectedGroup = ''
this.showTownPicker = false
},
showTown() {
console.log('s')
this.showTownPicker = true
},
showVillage() {
if (this.selectedTown) {
this.showVillagePicker = true
} else {
uni.showToast({
title: "请先选择村镇/街",
icon: 'none'
})
}
},
showGroup() {
if (this.selectedVillage) {
this.showGroupPicker = true;
} else {
uni.showToast({
title: "请先选择村/社区",
icon: 'none'
})
}
},
// 村选择
onVillageConfirm(e) {
const village = e.value[0]
this.selectedVillage = village.label
// 找到对应组
this.groupList = village.children || []
this.selectedGroup = ''
this.showVillagePicker = false
},
// 组选
onGroupConfirm(e) {
const group = e.value[0]
this.selectedGroup = group.label
this.showGroupPicker = false
},
downCallback() {
this.mescroll.resetUpScroll()
},
async upCallback(page) {
this.total = 0
// this.loading = page.num === 1
try {
uni.showLoading();
this.isSerach = true;
// console.log(this.loading)
// const token = uni.getStorageSync('token')
const params = {
pageNo: page.num,
pageSize: page.size,
}
if (this.searchKeyword) {
params.keyword = this.searchKeyword
}
if (this.currentStatus !== '') {
params.submitStatus = this.currentStatus
}
if (this.selectedTown != '') {
params.town = this.selectedTown;
}
if (this.selectedVillage != '') {
params.village = this.selectedVillage;
}
if (this.selectedGroup != '') {
params.groupName = this.selectedGroup;
}
if (this.selectDoorNum != '') {
params.doorNumber = this.selectDoorNum;
}
const response = await householdApi.pageHouseHoldPage(params)
if (response.total && response.total > 0) {
const newList = response.list;
this.total = response.total
if (page.num === 1) {
this.householdList = newList
} else {
this.householdList = [...this.householdList, ...newList]
}
this.mescroll.endBySize(newList.length, response.total || 0)
uni.hideLoading();
this.isSerach = false;
} else {
this.mescroll.endErr()
uni.hideLoading();
this.isSerach = false;
}
} catch (error) {
uni.hideLoading();
this.isSerach = false;
this.mescroll.endErr()
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
uni.hideLoading();
this.isSerach = false;
}
},
viewDetail(item) {
uni.navigateTo({
url: `/packageForm/pages/householdForm?id=${item.id}`
})
},
formatTime(timeStr) {
if (!timeStr) return ''
const date = new Date(timeStr)
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
}
}
}
</script>
<style scoped>
.container {
min-height: 100vh;
background: #f4f6fa;
}
.header {
background: #fff;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.search-box {
/* 搜索框样式 */
}
.input-button-container {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 20rpx;
}
.search-input-wrapper {
flex: 3;
}
.scan-button {
background: none;
border: none;
padding: 0 10rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8rpx;
background-color: #f5f5f5;
}
.search-button-wrapper {
flex: 1;
}
.content {
padding: 15rpx;
}
.filter-tabs {
display: flex;
background: #fff;
border-radius: 16rpx;
margin-bottom: 15rpx;
overflow: hidden;
}
.tab-item {
flex: 1;
text-align: center;
padding: 24rpx 0;
font-size: 28rpx;
color: #666;
transition: all 0.3s;
border-left: 1rpx solid #F5F3F2;
/* 灰色边框 */
/* 添加上边框 */
border-top: 1rpx solid #F5F3F2;
/* 添加下边框 */
border-bottom: 1rpx solid #F5F3F2;
border-right: 1rpx solid #F5F3F2;
}
.tab-item.active {
background: #2979ff;
color: #fff;
}
.list-container {
min-height: 400rpx;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 0;
color: #999;
font-size: 28rpx;
}
.household-list {
margin-bottom: 32rpx;
}
.household-item {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.householder-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.status-tag {
padding: 8rpx 16rpx;
border-radius: 12rpx;
font-size: 24rpx;
}
.status-tag.submitted {
background: #e8f5e8;
color: #19be6b;
}
.status-tag.draft {
background: #fff3e0;
color: #ff9500;
}
.item-info {
margin-bottom: 16rpx;
}
.info-text {
display: block;
font-size: 26rpx;
color: #666;
margin-bottom: 8rpx;
}
.item-footer {
border-top: 1rpx solid #eee;
padding-top: 16rpx;
}
.time-text {
font-size: 24rpx;
color: #999;
}
.area-select-row {
display: flex;
margin-top: 20rpx;
flex-direction: row;
}
.select-item1 {
flex: 1;
}
.select-item {
width: 32%;
margin-right: 2%;
}
</style>