This commit is contained in:
TS-QD1 2023-11-02 18:37:08 +08:00
commit 4b906b469e
25 changed files with 3358 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist.zip
dist-ssr
*.local
# Editor directories and files
.vscode
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

1982
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "btgxq-population-housing",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@vicons/fluent": "^0.12.0",
"@vicons/utils": "^0.1.4",
"axios": "^1.6.0",
"naive-ui": "^2.35.0",
"stylus": "^0.60.0",
"vue": "^3.3.4",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"vite": "^4.4.5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" width="15" height="15" style="border-color: rgba(0,0,0,0);border-width: bpx;border-style: undefined" filter="none">
<g>
<path d="M32 6.88c0-1.12-2.176-2.592-4.864-3.040-3.338-0.58-7.182-0.912-11.104-0.912s-7.766 0.332-11.506 0.969l0.402-0.057c-2.72 0.448-4.896 1.92-4.896 3.040 0 0.032 0.032 0.064 0.032 0.096-0.038 0.104-0.061 0.225-0.064 0.351l-0 0.001v3.776c0 0.512 0.448 0.96 0.992 0.96h4.928c0.544 0 0.992-0.448 0.992-0.96v-3.456c6.016-0.8 12.16-0.8 18.176 0v3.68c0 0.544 0.448 0.96 0.992 0.96h4.928c0.544 0 0.992-0.416 0.992-0.96v-3.936c0-0.096-0.032-0.192-0.064-0.288 0.032-0.064 0.064-0.128 0.064-0.224zM29.472 18.464l-5.408-1.824c-0.416-0.128-0.992-0.256-1.568-0.384v-5.984h-2.272v3.52h-8.384v-3.52h-2.304v6.016c-0.512 0.128-0.992 0.224-1.376 0.352l-5.28 1.792c-1.024 0.352-1.856 1.536-1.856 2.624v6.048c-0 0.004-0 0.008-0 0.012 0 1.071 0.869 1.94 1.94 1.94 0.004 0 0.008 0 0.013-0h26.399c0.004 0 0.008 0 0.012 0 1.071 0 1.94-0.869 1.94-1.94 0-0.004 0-0.008-0-0.013v0.001-6.048c0-1.088-0.832-2.272-1.856-2.592zM15.872 26.88c-2.944 0-5.312-2.4-5.312-5.344s2.368-5.344 5.312-5.344c2.976 0 5.344 2.4 5.344 5.344s-2.368 5.344-5.344 5.344zM15.872 18.688c-1.568 0-2.848 1.28-2.848 2.848s1.28 2.848 2.848 2.848c1.6 0 2.88-1.28 2.88-2.848s-1.28-2.848-2.88-2.848z" fill="rgba(233.07000000000002,157.07999999999998,66.045,1)"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="16" height="16" style="border-color: rgba(0,0,0,0);border-width: bpx;border-style: undefined" filter="none">
<g>
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="rgba(129.03,179.01,55.08,1)"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

7
src/App.vue Normal file
View File

@ -0,0 +1,7 @@
<template>
<router-view></router-view>
</template>
<script setup></script>
<style scoped></style>

1
src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,10 @@
<template>
</template>
<script setup>
</script>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,10 @@
<template>
</template>
<script setup>
</script>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,278 @@
<template>
<n-space class="container" vertical>
<n-space class="title" align="center">
<label>以人找房</label>
<div class="search">
<input type="text" v-model="keywords" placeholder="请输入人名以查找房">
<Icon class="search-btn" @click="onSearchClick">
<Search24Filled />
</Icon>
</div>
</n-space>
<n-space class="body">
<div class="no-data" v-if="dataRows.length === 0">暂无数据</div>
<n-grid x-gap="30" :y-gap="15" :cols="2" v-if="dataRows.length > 0">
<n-grid-item v-for="data in dataRows" :key="data">
<n-space class="card" vertical justify="between">
<div class="top">
<n-image width="60" height="60" src="assets/images/house.png" :preview-disabled="true" />
<div class="info">
<div class="line">
<span class="title">{{ data.houseDTO.communityName }}</span>
<span class="type">{{ data.houseDTO.houseType }}</span>
<span class="housing-estate">{{ data.houseDTO.residentialName }}</span>
<span class="population">
<n-image width="15" height="15" src="assets/images/md-person.svg" :preview-disabled="true" />
<span class="name">{{ data.houseDTO.homeowner }}</span>
</span>
</div>
<div class="line">
<span class="building-no">{{ data.houseDTO.buildingName }}/</span>
<span class="building-unit">{{ data.houseDTO.affiliationUnit }}单元/</span>
<span class="floor">{{ data.houseDTO.affiliationFloors }}</span>
<span class="room-no">{{ data.houseDTO.houseNum }}</span>
</div>
</div>
</div>
<n-space class="bottom">
<table>
<thead>
<tr>
<th>姓名</th>
<th>出生日期</th>
<th>证件类型</th>
<th>证件号码</th>
<th>联系方式</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="text" :title="data.populationInfoDTO.name">{{ data.populationInfoDTO.name }}</div>
</td>
<td>
<div class="text" :title="data.populationInfoDTO.birthday">{{ data.populationInfoDTO.birthday }}</div>
</td>
<td>
<div class="text" :title="data.populationInfoDTO.idcardType">{{ data.populationInfoDTO.idcardType }}</div>
</td>
<td>
<div class="text" :title="data.populationInfoDTO.idcard">{{ data.populationInfoDTO.idcard }}</div>
</td>
<td>
<div class="text" :title="data.populationInfoDTO.phone">{{ data.populationInfoDTO.phone }}</div>
</td>
</tr>
</tbody>
</table>
</n-space>
<div class="divider"></div>
</n-space>
</n-grid-item>
</n-grid>
</n-space>
<n-space class="footer">
<Pager :page="currentPage" :rows="rows" :total="total" @change="onPageChange" :key="pagerUpdateIndex"/>
</n-space>
</n-space>
</template>
<script setup>
import { ref, reactive, inject } from 'vue';
import { useRoute } from 'vue-router';
import { NSpace, NInput, NGrid, NGridItem, NImage } from 'naive-ui';
import { Search24Filled } from '@vicons/fluent';
import { Icon } from '@vicons/utils';
import Pager from '../pager/Pager.vue'
const route = useRoute();
const axios = inject('axios');
const keywords = ref(route.query.keywords);
const currentPage = ref(1);
const total = ref(0);
const rows = ref(10);
const pagerUpdateIndex = ref(0);
const dataRows = reactive([]);
const init = () => {
axios.get('/api/population/findHouse', {
params: {
keywords: keywords.value,
page: currentPage.value,
rows: rows.value
}
}).then(res => {
let data = res.data;
currentPage.value = data.page;
total.value = data.total;
dataRows.splice(0, dataRows.length);
dataRows.push(...data.rows);
pagerUpdateIndex.value++;
}).catch(resp => {
console.log()
});
}
const onSearchClick = () => {
currentPage.value = 1;
init();
}
const onPageChange = (pager) => {
currentPage.value = pager.page;
rows.value = pager.rows;
init();
}
init();
</script>
<style lang="stylus" scoped>
.container
.title
border-bottom 1px solid #FFFFFF
padding 10px 0
label
font-size 20px
a
font-size 13px
.search
border-radius 3px
position relative
.search-btn
font-size 30px
padding 3px
color #525e71
position absolute
top 1px
right 0px
cursor pointer
input
width 320px
padding 0 40px 0 15px
height 32px
border none
outline none
.body
width 840px
min-height 10px
padding 10px 0
.no-data
width 840px
text-align center
.card
width 385px
padding 10px
background-color rgba(255, 255, 255, 0.75)
border-radius 10px
position relative
cursor pointer
transition box-shadow 0.3s
&:hover
box-shadow 3px 3px 6px rgba(0,0,0,0.3)
.top
display flex
align-items center
text-align left
.info
color #000000
font-size 12px
width 380px
margin-left 10px
.line
display flex
align-items center
justify-content start
span
margin-right 10px
.title
font-size 14px
font-weight bold
border none
padding 0
.type
background-color #4095E5
padding 2px 10px
color #FFFFFF
border-radius 15px
transform scale(0.8)
.population
display flex
align-items center
.name
margin-left 5px
margin-right 0
.tags
color #FFFFFF
width 320px
height 38px
display -webkit-box
-webkit-box-orient vertical
overflow hidden
-webkit-line-clamp 2
span
margin-right 3px
background-color red
padding 1px 10px
border-radius 10px
font-size 12px
transform scale(0.8)
& .orange
background-color orange
& .yellow
background-color yellow
color black
& .blue
background-color blue
& .green
background-color green
& .grey
background-color grey
.bottom
margin-top 14px
table
width 386px
border 0
border-collapse collapse
thead
background-color rgba(129, 179, 55, 0.23)
color #567722
tbody
.text
width 75px
overflow hidden
text-overflow ellipsis
white-space nowrap
color #333333
transform scale(0.8)
.divider
border 1px dotted #FEFEFE
position absolute
top 74px
left 0px
width 404px
&:before
content ' '
display block
width 10px
height 10px
background-color rgba(52, 88, 140, 1)
position absolute
left -6px
top -5px
border-radius 10px
&:after
content ' '
display block
width 10px
height 10px
background-color rgba(52, 88, 140, 1)
position absolute
right -6px
top -5px
border-radius 10px
.footer
border-top 1px solid #FFFFFF
padding 10px 0
</style>

View File

@ -0,0 +1,291 @@
<template>
<n-space class="container" vertical>
<n-space class="title" align="center">
<label>以房找人</label>
<a href="javascript:void(0)" @click="onReseachClick">重新搜索</a>
</n-space>
<n-space class="body">
<div class="no-data" v-if="dataRows.length === 0">暂无数据</div>
<n-grid x-gap="30" :y-gap="15" :cols="2" v-if="dataRows.length > 0">
<n-grid-item v-for="data in dataRows" :key="data">
<n-space class="card" vertical justify="between">
<div class="top">
<n-image width="60" height="60" src="assets/images/women.png" :preview-disabled="true" v-if="data.sex === '女性'"/>
<n-image width="60" height="60" src="assets/images/man.png" :preview-disabled="true" v-else/>
<div class="info">
<div class="line">
<span class="name">{{ data.populationInfoHouseDTO.name }}</span>
<span class="sex" :class="{ female: data.populationInfoHouseDTO.sex === '女性' }">{{ data.populationInfoHouseDTO.sex }}</span>
<span class="age">17</span>
<span class="id-card">
<span class="type">{{ data.populationInfoHouseDTO.idcardType }}</span>
<span class="num">{{ data.populationInfoHouseDTO.idcard }}</span>
</span>
</div>
<div class="line">
<span class="birth">{{ data.populationInfoHouseDTO.birthday }}</span>
<span class="phone">
<n-image width="15" height="15" src="assets/images/dial-phone.svg"
:preview-disabled="true" />
<span class="num">{{ data.populationInfoHouseDTO.phone }}</span>
</span>
<span class="nation">{{ data.populationInfoHouseDTO.nation }}</span>
<span class="education">{{ data.populationInfoHouseDTO.education }}</span>
</div>
<div class="tags">
<span class="yellow" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isCorrect === '是'">社区矫正</span>
<span class="yellow" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isRelease === '是'">刑满释放</span>
<span class="grey" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isDispute === '是'">矛盾纠纷</span>
<span class="grey" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isSecurity === '是'">社会治安</span>
<span class="grey" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isDrug === '是'">吸毒人员</span>
<span class="grey" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isCult === '是'">涉邪人员</span>
<span class="grey" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isPetition === '是'">重点上访户</span>
<span class="red" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isSeriousIllness === '是'">重病</span>
<span class="orange" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isOldage === '是'">老年人</span>
<span class="blue" v-if="data.populationInfoHouseDTO.populationInfoBaseDTO.isMinority === '是'">未成年</span>
<span class="green" v-if="data.populationInfoHouseDTO.isNormal">正常</span>
</div>
</div>
</div>
<n-space class="bottom">
<table>
<thead>
<tr>
<th>社区</th>
<th>小区</th>
<th>/</th>
<th>单元/</th>
<th>/</th>
<th>门牌号</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="text">{{ data.houseDTO.communityName }}</div>
</td>
<td>
<div class="text">{{ data.houseDTO.residentialName }}</div>
</td>
<td>
<div class="text">{{ data.houseDTO.buildingName }}</div>
</td>
<td>
<div class="text">{{ data.houseDTO.affiliationUnit }}</div>
</td>
<td>
<div class="text">{{ data.houseDTO.affiliationFloors }}</div>
</td>
<td>
<div class="text">{{ data.houseDTO.houseNum }}</div>
</td>
</tr>
</tbody>
</table>
</n-space>
<div class="divider"></div>
</n-space>
</n-grid-item>
</n-grid>
</n-space>
<n-space class="footer">
<Pager :page="currentPage" :rows="rows" :total="total" @change="onPageChange" :key="pagerUpdateIndex"/>
</n-space>
</n-space>
</template>
<script setup>
import { ref, reactive, inject } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { NSpace, NInput, NGrid, NGridItem, NImage } from 'naive-ui';
import Pager from '../pager/Pager.vue'
const route = useRoute();
const router = useRouter();
const axios = inject('axios');
const query = route.query;
const currentPage = ref(1);
const total = ref(0);
const rows = ref(10);
const pagerUpdateIndex = ref(0);
const dataRows = reactive([]);
const init = () => {
axios.get('/api/population/findPopulation', {
params: {
...query,
page: currentPage.value,
rows: rows.value
}
}).then(res => {
let data = res.data;
currentPage.value = data.page;
total.value = data.total;
dataRows.splice(0, dataRows.length);
data.rows.forEach(item => {
item.populationInfoHouseDTO.isNormal = (
item.populationInfoHouseDTO.populationInfoBaseDTO.isCorrect === '否' &&
item.populationInfoHouseDTO.populationInfoBaseDTO.isRelease === '否' &&
item.populationInfoHouseDTO.populationInfoBaseDTO.isDispute === '否' &&
item.populationInfoHouseDTO.populationInfoBaseDTO.isSecurity === '否' &&
item.populationInfoHouseDTO.populationInfoBaseDTO.isDrug === '否' &&
item.populationInfoHouseDTO.populationInfoBaseDTO.isCult === '否' &&
item.populationInfoHouseDTO.populationInfoBaseDTO.isPetition === '否'
)
dataRows.push(item);
});
pagerUpdateIndex.value++;
}).catch(resp => {
});
}
const onPageChange = (pager) => {
currentPage.value = pager.page;
rows.value = pager.rows;
init();
}
const onReseachClick = () => {
router.push({
path: 'population-search'
})
}
init();
</script>
<style lang="stylus" scoped>
.container
.title
border-bottom 1px solid #FFFFFF
padding 10px 0
label
font-size 20px
a
color #eeeeee
font-size 13px
.body
width 840px
min-height 10px
padding 10px 0
.no-data
width 840px
text-align center
.card
width 385px
padding 10px
background-color rgba(255, 255, 255, 0.75)
border-radius 10px
position relative
cursor pointer
transition box-shadow 0.3s
&:hover
box-shadow 3px 3px 6px rgba(0,0,0,0.3)
.top
display flex
align-items center
text-align left
.info
color #000000
font-size 12px
width 380px
.line
display flex
align-items center
justify-content space-between
.name
font-size 14px
font-weight bold
.sex
background-color #4095E5
padding 2px 10px
color #FFFFFF
border-radius 15px
transform scale(0.8)
.female
background-color #FF919C
.id-card
.type
margin-right 5px
.phone
display flex
align-items center
.num
margin-left 5px
margin-right 0
.tags
color #FFFFFF
width 320px
height 38px
display -webkit-box
-webkit-box-orient vertical
overflow hidden
-webkit-line-clamp 2
span
margin-right 3px
background-color red
padding 1px 10px
border-radius 10px
font-size 12px
transform scale(0.8)
& .orange
background-color orange
& .yellow
background-color yellow
color black
& .blue
background-color blue
& .green
background-color green
& .grey
background-color grey
.bottom
padding-top 14px
table
width 386px
border 0
border-collapse collapse
thead
background-color rgba(129, 179, 55, 0.23)
color #567722
tbody
.text
width 62px
text-align center
overflow hidden
text-overflow ellipsis
white-space nowrap
color #333333
transform scale(0.8)
.divider
border 1px dotted #FEFEFE
position absolute
top 100px
left 0px
width 404px
&:before
content ' '
display block
width 10px
height 10px
background-color rgba(52, 88, 140, 1)
position absolute
left -6px
top -5px
border-radius 10px
&:after
content ' '
display block
width 10px
height 10px
background-color rgba(52, 88, 140, 1)
position absolute
right -6px
top -5px
border-radius 10px
.footer
border-top 1px solid #FFFFFF
padding 10px 0
</style>

View File

@ -0,0 +1,171 @@
<template>
<div class="container">
<span class="btn first" v-if="currentPage > 1" @click="onPageClick(1)">
<Icon>
<ArrowPrevious20Regular/>
</Icon>
</span>
<span class="btn before" v-if="currentPage > 1" @click="onPageClick(currentPage - 1)">
<Icon>
<ChevronLeft20Filled/>
</Icon>
</span>
<div class="btn-page">
<span class="btn" :class="{'active': currentPage === page}" v-for="page in pagerBtnArray" :key="page" @click="onPageClick(page)">{{ page }}</span>
</div>
<span class="btn after" v-if="currentPage < totalPage" @click="onPageClick(currentPage + 1)">
<Icon>
<ChevronRight20Filled/>
</Icon>
</span>
<span class="btn last" v-if="currentPage < totalPage" @click="onPageClick(totalPage)">
<Icon>
<ArrowNext20Filled/>
</Icon>
</span>
<n-select size="small" v-model:value="pageSize.value" v-if="totalPage > 0" :options="pageSize.options" @update:value="onPageSizeUpdate"/>
<div class="jump" v-if="totalPage > 0">
<span>跳转</span>
<n-input-number size="small" placeholder="请输入" v-model:value="jumpPage.value" :min="jumpPage.min" :show-button="false"/>
<span></span>
<n-button @click="onPageJumpConfirmClick">确定</n-button>
</div>
<span> {{ totalPage }} {{ props.total }} 条数据</span>
</div>
</template>
<script setup>
import {ref, reactive, defineProps, defineEmits} from 'vue';
import {ArrowPrevious20Regular, ChevronLeft20Filled, ChevronRight20Filled, ArrowNext20Filled} from '@vicons/fluent';
import {Icon} from '@vicons/utils';
import {NSelect, NInputNumber, NButton} from 'naive-ui';
const props = defineProps({
page: {
type: Number,
default: 1
},
rows: {
type: Number,
default: 10
},
total: {
type: Number,
default: 0
},
});
const emits = defineEmits(['change']);
const pageSize = reactive({
value: props.rows,
options: [
{label: '10条/页', value: 10},
{label: '20条/页', value: 20},
{label: '40条/页', value: 40},
{label: '60条/页', value: 60},
{label: '80条/页', value: 80},
{label: '100条/页', value: 100},
{label: '200条/页', value: 200},
]
});
const jumpPage = reactive({
value: 1,
min: 1,
});
//
const pagerSize = 5;
//
const pagerHalfSize = pageSize % 2 === 0 ? parseInt(pagerSize / 2) : parseInt((pagerSize + 1) / 2);
//
const totalPage = ref(props.total % pageSize.value === 0 ? parseInt(props.total / pageSize.value) : parseInt(props.total / pageSize.value + 1));
const currentPage = ref(props.page);
const pagerBtnArray = reactive([]);
const renderPagerBtn = () => {
pagerBtnArray.splice(0, pagerBtnArray.length);
if(currentPage.value < pagerHalfSize) {
let pageBtnLength = pagerSize > totalPage.value ? totalPage.value : pagerSize;
for(let i = 0; i < pageBtnLength; i++) {
pagerBtnArray.push(i + 1);
}
return;
}
if(currentPage.value > totalPage.value - pagerHalfSize) {
for(let i = 0; i < pagerSize; i++) {
pagerBtnArray.push(totalPage.value - pagerSize + i + 1);
}
return;
}
for(let i = 0; i < pagerSize; i++) {
pagerBtnArray.push(currentPage.value - pagerHalfSize + i + 1);
}
}
renderPagerBtn();
const onPageClick = (page) => {
renderPagerBtn();
emits('change', {
page: page,
rows: pageSize.value
});
}
const onPageSizeUpdate = (value, option) => {
emits('change', {
page: currentPage.value,
rows: value
});
}
const onPageJumpConfirmClick = () => {
if(!jumpPage.value) {
return;
}
emits('change', {
page: jumpPage.value,
rows: pageSize.value
});
}
</script>
<style lang="stylus" scoped>
.container
font-size 14px
display flex
align-items center
user-select none
.btn
height 26px
line-height 26px
display inline-block
padding 0 10px
border-radius 5px
margin 0 2px
cursor pointer
&.active
color #3894FF
&.first
&.before
&.after
&.last
display flex
justify-content center
align-items center
font-size 18px
min-width 30px
border 1px solid #FFFFFF
.jump
padding 0 5px
display flex
align-items center
.n-input-number
margin 0 3px
width 60px
.n-button
height 28px
color #FFFFFF
margin-left 5px
.n-select
width 120px
margin 0 2px
</style>

View File

@ -0,0 +1,66 @@
<template>
<div class="container">
<div class="title">以人找房</div>
<div class="search">
<input type="text" placeholder="请输入人名以查找房" v-model="keywords">
<Icon class="search-btn" @click="onSearchClick">
<Search24Filled />
</Icon>
</div>
<div class="notes">请输入关键字姓名身份证手机号等</div>
</div>
</template>
<script setup>
import { ref, inject } from 'vue';
import { useRouter } from 'vue-router';
import { Search24Filled } from '@vicons/fluent';
import { Icon } from '@vicons/utils';
const router = useRouter();
const keywords = ref('');
const onSearchClick = () => {
router.push({
path: '/house-list',
query: {
keywords: keywords.value
}
})
}
</script>
<style lang="stylus" scoped>
.container
margin-top 200px
display flex
flex-direction column
align-items center
justify-content center
.title
font-size 28px
font-weight bold
.search
margin-top 30px
border 2px solid #03DCDCDC
border-radius 3px
position relative
.search-btn
font-size 30px
padding 3px
color #525e71
position absolute
top 1px
right 0px
cursor pointer
input
width 600px
padding 0 40px 0 15px
height 37px
border none
outline none
.notes
margin-top 22px
font-size 12px
</style>

View File

@ -0,0 +1,353 @@
<template>
<div class="container">
<div class="title">以房找人</div>
<div class="search">
<n-grid x-gap="30" :y-gap="15" :cols="3">
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>所在街道</label>
<n-select v-model:value="street.value" :options="street.options" @update:value="onStreetUpdate"/>
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>所在社区</label>
<n-select v-model:value="village.value" :options="village.options" @update:value="onVillageUpdate"/>
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>所在小区</label>
<n-select v-model:value="housingEstate.value" :options="housingEstate.options" @update:value="onHousingEstateUpdate"/>
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>/牌名称</label>
<n-select v-model:value="building.value" :options="building.options" @update:value="onBuildingUpdate"/>
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>所在单元/</label>
<n-select v-model:value="buildingUnit.value" :options="buildingUnit.options" />
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>所在院层</label>
<n-select v-model:value="buildingFloor.value" :options="buildingFloor.options" />
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>门牌号</label>
<n-input v-model:value="houseNumber" placeholder="请输入门牌号" />
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>是否出租房</label>
<n-select v-model:value="rentalHousing.value" :options="rentalHousing.options" />
</n-space>
</n-grid-item>
<n-grid-item>
<n-space class="search-condition" justify="space-between" align="center">
<label>是否空置房</label>
<n-select v-model:value="vacantHouse.value" :options="vacantHouse.options" />
</n-space>
</n-grid-item>
</n-grid>
</div>
<div class="btn">
<n-button @click="onSearchClick">
<Icon class="icon">
<Search24Filled />
</Icon>
<label>搜索</label>
</n-button>
</div>
</div>
</template>
<script setup>
import { inject } from 'vue';
import { useRouter } from 'vue-router';
import { NSpace, NGrid, NGridItem, NSelect, NInput, NButton } from 'naive-ui';
import { reactive, ref } from 'vue';
import { Search24Filled } from '@vicons/fluent';
import { Icon } from '@vicons/utils';
import { isSlotEmpty } from 'naive-ui/es/_utils';
const router = useRouter();
const axios = inject('axios');
//
const street = reactive({
value: 'song1',
options: [{
label: '无',
value: ''
}]
});
//
const village = reactive({
value: '',
options: [{
label: '无',
value: ''
}]
});
//
const housingEstate = reactive({
value: '',
options: [{
label: '无',
value: ''
}]
});
//
const building = reactive({
value: '',
options: [{
label: '无',
value: ''
}]
});
//
const buildingUnit = reactive({
value: '',
options: [{
label: '无',
value: ''
}]
});
//
const buildingFloor = reactive({
value: '',
options: [{
label: '无',
value: '',
floors: '',
units: ''
}]
});
//
const houseNumber = ref('');
//
//
const rentalHousing = reactive({
value: '',
options: [{
label: '无',
value: ''
}, {
label: '是',
value: 1
}, {
label: '否',
value: 0
}]
})
//
const vacantHouse = reactive({
value: '',
options: [{
label: '无',
value: ''
}, {
label: '是',
value: 1
}, {
label: '否',
value: 0
}]
})
//
const initStreet = () => {
street.value = '';
street.options.splice(1, street.options.length - 1);
axios.get('/api/residential/getStreetList', {}).then(res => {
let data = res.data;
data.forEach(item => {
street.options.push({
label: item.name,
value: item.id
})
});
}).catch(res => {});
}
//
const initVillage = (areaId) => {
village.value = '';
village.options.splice(1, village.options.length - 1);
if(!areaId) {
return;
}
axios.get('/api/residential/getCommunityList', {
params: {
areaId: areaId
}
}).then(res => {
let data = res.data;
data.forEach(item => {
village.options.push({
label: item.communityName,
value: item.communityId
})
});
}).catch(res => {});
}
//
const initHousingEstate = (communityId) => {
housingEstate.value = '';
housingEstate.options.splice(1, housingEstate.options.length - 1);
if(!communityId) {
return;
}
axios.get('/api/residential/list', {
params: {
street: street.value,
community: communityId
}
}).then(res => {
let data = res.data;
data.forEach(item => {
housingEstate.options.push({
label: item.name,
value: item.residentialId
})
});
}).catch(res => {});
}
// /
const initBuilding = (residentialId) => {
building.value = '';
building.options.splice(1, building.options.length - 1);
if(!residentialId) {
return;
}
axios.get('/api/building/list', {
params: {
residentialId: residentialId
}
}).then(res => {
let data = res.data;
data.forEach(item => {
building.options.push({
label: item.name,
value: item.buildingId,
floors: item.floorsNum,
units: item.unitNum
})
});
}).catch(res => {});
}
// /
const initBuildingUnit = (buildingUnitSize) => {
buildingUnit.value = '';
buildingUnit.options.splice(1, buildingUnit.options.length - 1);
if(!buildingUnitSize) {
return;
}
for(let i = 1; i <= buildingUnitSize; i++) {
buildingUnit.options.push({
label: `${i}单元/列`,
value: i
})
}
}
//
const initBuildingFloor = (buildingFloorSize) => {
buildingFloor.value = '';
buildingFloor.options.splice(1, buildingFloor.options.length - 1);
if(!buildingFloorSize) {
return;
}
for(let i = 1; i <= buildingFloorSize; i++) {
buildingFloor.options.push({
label: `${i}层/院`,
value: i
})
}
}
//
const onStreetUpdate = (value, option) => {
initVillage(value);
initHousingEstate();
initBuilding();
initBuildingUnit();
initBuildingFloor();
}
//
const onVillageUpdate = (value, option) => {
initHousingEstate(value);
initBuilding();
initBuildingUnit();
initBuildingFloor();
}
//
const onHousingEstateUpdate = (value, option) => {
initBuilding(value);
initBuildingUnit();
initBuildingFloor();
}
//
const onBuildingUpdate = (value, option) => {
initBuildingUnit(value ? option.units : null);
initBuildingFloor(value ? option.floors : null);
}
const onSearchClick = () => {
router.push({
path: '/population-list',
query: {
street: street.value,
community: village.value,
residentialId: housingEstate.value,
buildingId: building.value,
affiliationUnit: buildingUnit.value,
affiliationFloors: buildingFloor.value,
houseNum: houseNumber.value,
isRental: rentalHousing.value,
isVacant: vacantHouse.value
}
})
}
initStreet();
</script>
<style lang="stylus" scoped>
.container
margin-top 200px
display flex
flex-direction column
justify-content center
align-content center
text-align center
.title
font-size 28px
font-weight bold
.search
margin-top 30px
.search-condition
.n-input
.n-select
border-radius 50px
overflow hidden
width 168px
.btn
margin-top 60px
.n-button
width 240px
height 30px
border-radius 15px
background-color #FFBF6B
color #FFFFFF
.icon
font-size 18px
label
margin-left 5px
cursor pointer
</style>

19
src/main.js Normal file
View File

@ -0,0 +1,19 @@
import { createApp } from 'vue'
import {router} from './route/route'
import './style.css'
import App from './App.vue'
import axios from 'axios';
const createAxios = {
install(app, options) {
axios.defaults.baseURL = 'http://192.168.0.9:7023/population';
// axios.defaults.baseURL = '/population';
axios.defaults.timeout = 20000;
app.provide('axios', axios);
}
}
const app = createApp(App);
app.use(router);
app.use(createAxios);
app.mount('#app');

39
src/route/route.js Normal file
View File

@ -0,0 +1,39 @@
import { createRouter, createWebHashHistory } from 'vue-router';
import PopulationSearch from '../components/search/PopulationSearch.vue';
import HouseSearch from '../components/search/HouseSearch.vue';
import PopulationList from '../components/list/PopulationList.vue';
import HouseList from '../components/list/HouseList.vue';
import PopulationInfo from '../components/info/PopulationInfo.vue';
import HouseInfo from '../components/info/HouseInfo.vue';
const routes = [{
name: '以人找房搜索',
path: '/population-search',
component: PopulationSearch
}, {
name: '以房找人搜索',
path: '/house-search',
component: HouseSearch
}, {
name: '以房找人列表',
path: '/population-list',
component: PopulationList
}, {
name: '以人找房列表',
path: '/house-list',
component: HouseList
}, {
name: '人口详情',
path: '/population-info',
component: PopulationInfo
}, {
name: '房屋详情',
path: '/house-info',
component: HouseInfo
}]
export const router = createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: createWebHashHistory(),
routes, // `routes: routes` 的缩写
})

40
src/style.css Normal file
View File

@ -0,0 +1,40 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: rgba(52, 88, 140, 1);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
html {
min-height: 100%;
}
body {
margin: 0;
min-width: 320px;
min-height: 100%;
color: #FFFFFF;
}
#app {
height: 100%;
max-width: 840px;
margin: 0 auto;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: rgba(52, 88, 140, 1);
}
a:hover {
color: #747bff;
}
}

8
vite.config.js Normal file
View File

@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
base: './',
plugins: [vue()],
})