rhb-server/mes-ui/rhb-app/packageQuery/pages/query.vue
2025-10-20 11:14:41 +08:00

643 lines
16 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>