Files
estate/src/views/workbench/index2.vue
2026-04-18 18:14:07 +08:00

872 lines
24 KiB
Vue
Raw 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>
<!-- 外层缩放容器 -->
<div class="scale-container">
<div class="w-full h-full workBenchBox">
<el-row>
<el-col :span="18">
<div class="villageBox">
<div class="title">小区概况</div>
<div class="villageBody">
<div class="village_items" v-for="(item, index) in list" :key="index">
<div class="icon">
<el-image :src="item.icon"></el-image>
</div>
<div class="infobox">
<div class="infoTitle">{{ item.name }}</div>
<div class="infoNum">{{ item.value }}</div>
</div>
</div>
</div>
</div>
<div class="echartsbox flex flex-items-center">
<div class="echarts">
<div class="nav flex flex-items-center justify-between">
<div>
<span class="ecarts_title">近一周报修量趋势</span>
<span class="echarts_units">单位</span>
</div>
<div class="flex flex-items-center justify-center">
<span class="echarts_all_baoxiu">全部保修</span>
<i class="echarts_all_arrow"></i>
</div>
</div>
<div class="echartsbody">
<repairEcharts />
</div>
</div>
<div class="echarts">
<div class="nav flex flex-items-center justify-between">
<div>
<span class="ecarts_title">近一周收入支出趋势</span>
<span class="echarts_units">单位</span>
</div>
<div class="flex flex-items-center justify-center">
<div
class="actions"
@click="changeActive_index(index)"
:class="{ active: active_index === index }"
v-for="(item, index) in actions"
:key="index"
>
{{ item.name }}
</div>
</div>
</div>
<div class="echartsbody">
<Echarts :options="chartConfig" />
</div>
</div>
</div>
</el-col>
<!-- tabs -->
<el-col :span="6">
<div class="villageInfoBox">
<div class="iconbox">
<el-image :src="summicon"></el-image>
</div>
<div class="villageinfo">
<div class="villagename">{{ villageinfo.villageName }}</div>
<div class="week">{{ new Date().toLocaleDateString().replaceAll('/', '-') }} {{ numToWeek2(new Date().getDay()) }}</div>
</div>
</div>
<div class="tabsbox">
<div class="topbox">
<div
:class="{
active: Sideactive === 1
}"
@click="handleSelect(1)"
>
<span>快捷入口</span>
<i></i>
</div>
<div
:class="{
active: Sideactive === 2
}"
@click="handleSelect(2)"
>
<span>创建办事记录</span>
<i class="righti"></i>
</div>
</div>
<div class="body">
<div class="quicly_box" v-if="Sideactive === 1">
<div class="quickly_item" v-for="(item, index) in quickBox" :key="index" @click="handleRouter(item.url)">
<div class="quickly_item_icon"></div>
<div class="quickly_item_content">{{ item.name }}</div>
</div>
<div class="quickly_item add">
<el-icon><Plus /></el-icon>
<span>添加</span>
</div>
</div>
<div v-else>
<div class="noticeNav">
<div>创建办事记录</div>
<el-button type="primary">提交</el-button>
</div>
<div class="noticeBox">
<el-form label-position="top">
<el-form-item label="需求方姓名">
<el-input placeholder="请输入姓名" v-model="noticForm.seekName"></el-input>
</el-form-item>
<el-form-item label="办事时间">
<el-date-picker
v-model="noticForm.handleTime"
type="datetime"
clearable
placeholder="年-月-日 时:分"
value-format="YYYY-MM-DD HH:MM"
format="YYYY-MM-DD HH:MM"
>
</el-date-picker>
</el-form-item>
<el-form-item label="办事内容">
<el-input type="textarea" v-model="noticForm.content" :rows="2" placeholder="请输入办事内容"></el-input>
</el-form-item>
<el-form-item label="附件">
<el-upload
v-model:file-list="fileList"
class="upload-demo"
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
multiple
>
<div class="uploadBtn flex flex-items-center">
<el-icon><Upload /></el-icon>
<span>上传文件</span>
</div>
</el-upload>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</el-col>
</el-row>
<!-- table -->
<el-row class="mt-20px">
<el-col>
<div class="userBox">
<div class="title">住户查询</div>
<div class="searbox">
<div>精准查询</div>
<el-select v-model="seachervalue" placeholder="住户姓名" style="width: 240px">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-input v-model="searchInput" placeholder="请输入">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary" @click="handleSearch">查询 </el-button>
<el-button>重置</el-button>
</div>
<div class="tablebox">
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column type="index" :index="indexMethod" label="序号" width="60" align="center" />
<el-table-column prop="name" align="center" label="姓名" width="180" />
<el-table-column prop="type" align="center" label="类型" width="180">
<template #default="scope">
<DictTag :options="com_resident_type" :value="scope.row.status"></DictTag>
</template>
</el-table-column>
<el-table-column prop="status" align="center" label="状态" width="180">
<template #default="scope">
<DictTag :options="com_review" :value="scope.row.type"></DictTag>
</template>
</el-table-column>
<el-table-column prop="idCard" align="center" label="证件号码" />
<el-table-column prop="phone" align="center" label="手机号" />
<el-table-column prop="houseName" align="center" label="房间" />
<el-table-column prop="address" align="center" label="操作">
<template #default>
<el-button type="primary" text> 审核</el-button>
<el-button type="primary" text> 编辑</el-button>
<el-button type="primary" text> 删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:background="false"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script lang="ts" setup>
import summicon from '@/assets/images/index/summer.png';
import repairEcharts from './components/repairEcharts.vue';
import { listResident } from '@/api/system/resident';
import { useHouseStore } from '@/store/modules/house';
const houseStore = useHouseStore();
const villageinfo = ref({
'villageId': null,
'villageName': ''
});
function getVillageinfo() {
const id = Number(localStorage.getItem('villageid'));
getVillageByIdAPI(id).then((res) => {
villageinfo.value = res.data;
houseStore.updateVilageinfo(res.data);
});
}
getVillageinfo();
import icon1 from '@/assets/images/index/1.png';
import icon2 from '@/assets/images/index/2.png';
import icon3 from '@/assets/images/index/3.png';
import icon5 from '@/assets/images/index/5.png';
import icon6 from '@/assets/images/index/6.png';
import { UploadUserFile } from 'element-plus';
import { getVillageByIdAPI } from '@/api/system/community';
import { getLast7Days, numToWeek2 } from '@/utils/time';
import { EChartsOption } from 'echarts';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_resident_type } = toRefs<any>(proxy?.useDict('com_resident_type'));
const { com_review } = toRefs<any>(proxy?.useDict('com_review'));
const list = ref([
{ name: '住户总数量', value: 10000, icon: icon1 },
{ name: '房屋总数量', value: 458, icon: icon2 },
{ name: '车位总数量', value: 2539, icon: icon3 },
{ name: '设备总数量', value: 22, icon: icon5 },
{ name: '物料总数量', value: 458, icon: icon6 },
{ name: '投诉总数量', value: 458, icon: icon6 }
]);
// ! echarts 切换
const active_index = ref(0);
const actions = ref([
{
name: '全部'
},
{
name: '收入'
},
{
name: '支出'
}
]);
function changeActive_index(val: number) {
active_index.value = val;
}
const chartConfig = shallowRef<EChartsOption>({
grid: {
left: '1%',
right: '1%',
top: '10%',
bottom: '1%',
containLabel: true
},
tooltip: {
trigger: 'axis'
},
legend: {
top: 0,
data: ['收入', '支出']
},
xAxis: {
data: getLast7Days()
},
yAxis: {
type: 'value'
},
series: [
{
data: [87, 23, 105, 41, 69, 12, 94],
type: 'line',
smooth: true,
name: '收入',
itemStyle: {
color: '#1a75ff'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1, // 从上到下渐变
colorStops: [
{ offset: 0, color: '#e2edff' }, // 顶部:半透明蓝色
{ offset: 1, color: '#f2f9ff' } // 底部:极浅蓝/透明
]
}
},
lineStyle: {
color: '#1a75ff'
},
label: {
show: true,
color: '#3f8afc',
position: 'top'
}
},
{
data: [216, 150, 192, 210, 191, 219, 175].reverse(),
type: 'line',
smooth: true,
name: '支出',
itemStyle: {
color: '#46d3a4'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1, // 从上到下渐变
colorStops: [
{ offset: 0, color: '#e2edff' }, // 顶部:半透明蓝色
{ offset: 1, color: '#f2f9ff' } // 底部:极浅蓝/透明
]
}
},
lineStyle: {
color: '#46d3a4'
},
label: {
show: true,
color: '#46d3a4',
position: 'top'
}
}
]
});
// ! echarts 切换
// ! 侧边栏
const Sideactive = ref(1);
function handleSelect(val: number) {
Sideactive.value = val;
}
// ! 侧边栏
// ! 快捷入口
const quickBox = ref([
{
name: '新建用户',
url: '/house/resident'
},
{
name: '车位管理',
url: '/carArea/parking'
},
{
name: '新建月卡',
url: '/carArea/monthCard'
},
{
name: '新建报修',
url: '/repair/repairlist'
},
{
name: '访客记录',
url: '/repair/visitorList'
},
{
name: '账单管理',
url: '/repair/visitorList'
}
]);
const router = useRouter();
function handleRouter(val: string) {
router.push({
path: val
});
}
// ! 快捷入口
// ! 创建办事记录
const noticForm = ref({
seekName: '',
phone: '',
handleTime: '',
content: '',
handlePosition: '',
file: ''
});
const fileList = ref<UploadUserFile[]>([]);
// ! 创建办事记录
// ! table
const seachervalue = ref('');
const searchInput = ref('');
const loading = ref(true);
const total = ref(100);
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10
}
});
const { queryParams } = toRefs(data);
// 核心:计算连续序号
const indexMethod = (index) => {
return (queryParams.value.pageNum - 1) * queryParams.value.pageSize + index + 1;
};
const options = [
{
value: 'name',
label: '住户姓名'
},
{
value: 'phone',
label: '住户手机号'
},
{
value: 'idCard',
label: '住户身份证'
}
];
function handleSearch() {
queryParams[seachervalue.value] = searchInput.value;
getList();
}
const tableData = ref([]);
async function getList() {
const res = await listResident(queryParams.value);
tableData.value = res.rows;
}
getList();
// ! table
</script>
<style scoped lang="scss">
.workBenchBox {
padding: 20px;
background-color: #eaf2ff;
// 小区概况
.villageBox {
background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, #ffffff 70%);
border-radius: 16px 16px 16px 16px;
border: 2px solid #ffffff;
padding: 20px;
margin-bottom: 10px;
.title {
height: 25px;
font-weight: 500;
font-size: 20px;
color: #303133;
line-height: 25px;
margin-bottom: 20px;
}
.villageBody {
display: flex;
align-items: center;
justify-content: space-around;
flex-wrap: wrap; /* 自动换行 */
gap: 16px;
.village_items {
flex: 1;
min-width: 160px; /* 小屏自动单列 */
margin-right: 10px;
min-height: 109px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, #ffffff 100%);
box-shadow: 0px 4px 8px 0px rgba(0, 104, 255, 0.06);
border-radius: 8px 8px 8px 8px;
border: 1px solid #ffffff;
padding-left: 20px;
display: flex;
align-items: center;
.icon {
width: 40px;
height: 46px;
background: #f4f0ff;
box-shadow: inset 0px 0px 20px 0px #ffffff;
border-radius: 10px 10px 10px 10px;
border: 1px solid #ffffff;
margin-right: 15px;
.el-image {
width: 100%;
height: 100%;
}
}
.infobox {
margin-right: 20px;
.infoTitle {
height: 18px;
font-weight: 400;
color: #4e5969;
line-height: 18px;
font-size: 14px;
}
.infoNum {
height: 35px;
font-weight: 600;
font-size: 24px;
color: #303133;
line-height: 39px;
}
}
}
}
}
// echarts
.echartsbox {
height: inherit;
.echarts {
margin-right: 20px;
flex: 1;
height: 450px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, #ffffff 70%);
border: 2px solid #ffffff;
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
&:last-child {
margin-right: 0;
}
.nav {
.ecarts_title {
font-weight: 500;
font-size: 20px;
color: #303133;
line-height: 25px;
}
.echarts_units {
margin-left: 10px;
font-weight: 400;
font-size: 12px;
color: #98a4b2;
line-height: 15px;
text-align: left;
}
.echarts_all_baoxiu {
height: 18px;
font-weight: 400;
font-size: 14px;
color: #98a4b2;
line-height: 18px;
margin-right: 7px;
}
.echarts_all_arrow {
display: block;
width: 10px;
height: 10px;
border: 1px solid #ccc;
transform: rotateZ(45deg);
border-bottom-color: transparent;
border-left-color: transparent;
}
.actions {
width: 60px;
height: 22px;
border-radius: 4px 4px 4px 4px;
background-color: #f6f7fb;
color: #99a5b3;
text-align: center;
line-height: 22px;
font-size: 14px;
cursor: pointer;
margin-right: 10px;
&:hover {
color: #036aff;
background-color: #036cff25;
}
}
.actions.active {
color: white;
background-color: #036aff;
}
}
.echartsbody {
flex: 1;
margin-top: 20px;
}
}
}
// 小区
.villageInfoBox {
height: 127px;
margin-left: 20px;
display: flex;
align-items: center;
padding: 0 35px;
background: #036aff;
border-radius: 16px 16px 16px 16px;
margin-bottom: 20px;
.iconbox {
width: 70px;
height: 70px;
}
.villageinfo {
margin-left: 30px;
.villagename {
height: 30px;
font-weight: 500;
font-size: 24px;
color: #ffffff;
line-height: 30px;
}
.week {
margin-top: 5px;
height: 23px;
font-weight: 400;
font-size: 18px;
color: #ffffff;
line-height: 23px;
}
}
}
// tabs
.tabsbox {
margin-left: 20px;
border: 2px solid;
background: linear-gradient(180deg, #ebf4ff 0%, rgba(247, 252, 255, 0.5) 21.14%);
border-image: linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)) 2 2;
.topbox {
display: flex;
align-items: center;
div {
flex: 1;
border-bottom: 4px solid;
border-image: linear-gradient(to bottom, #fff, rgba(255, 255, 255, 0)) 100% 1;
text-align: center;
padding: 14px;
cursor: pointer;
color: #98a4b2;
&.active {
border-bottom-color: transparent;
border-image: none;
position: relative;
color: #000;
$width: 8px;
$greePer: 60%;
& i {
display: block;
position: absolute;
right: 10px;
top: 0;
width: calc($width / 2);
height: 100%;
transform: skew(30deg);
background: white;
transition: all 0.3s;
z-index: 10;
&::after {
content: '';
display: block;
width: $width;
height: $width;
background: radial-gradient(circle at bottom left, #ecf4fe $greePer, #ffffff (1 - $greePer));
position: absolute;
left: -$width;
top: 0;
}
&::before {
content: '';
display: block;
width: $width;
height: $width;
background: radial-gradient(circle at top right, #ecf4fe $greePer, #ffffff (1 - $greePer));
position: absolute;
right: -$width;
bottom: 0;
}
}
& i.righti {
display: block;
position: absolute;
left: 10px;
top: 0;
width: calc($width / 2);
height: 100%;
transform: skew(-30deg);
background: white;
z-index: 10;
&::after {
content: '';
display: block;
width: $width;
height: $width;
background: radial-gradient(circle at bottom right, #ecf4fe $greePer, #ffffff (1 - $greePer));
position: absolute;
right: -$width;
left: initial;
top: 0;
}
&::before {
content: '';
display: block;
width: $width;
height: $width;
background: radial-gradient(circle at top left, #ecf4fe $greePer, #ffffff (1 - $greePer));
position: absolute;
left: -$width;
right: none;
bottom: 0;
}
}
&::after {
content: '';
display: block;
position: absolute;
bottom: 0;
left: 50%;
width: 24px;
height: 4px;
background: #036aff;
border-radius: 3px 3px 3px 3px;
transform: translateX(-50%);
}
}
}
}
.body {
color: #222222;
padding: 20px;
font-size: 0.7rem;
.quicly_box {
display: flex;
align-items: center;
justify-content: start;
flex-wrap: wrap;
gap: 10px;
.quickly_item {
min-width: 30%;
max-width: 33%;
margin-bottom: 20px;
text-align: center;
height: 56px;
background: #ffffff;
border-radius: 12px 12px 12px 12px;
border: 1px solid #c0daff;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover {
background-color: #0084ff09;
}
.quickly_item_content {
margin-left: 5px;
width: 64px;
height: 23px;
font-weight: 500;
color: #222222;
line-height: 23px;
}
&.add {
text-align: center;
color: #036aff;
vertical-align: baseline;
span {
margin-left: 5px;
}
}
}
}
.noticeNav {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.noticeBox {
:deep(.el-date-editor.el-input, .el-date-editor.el-input__wrapper) {
width: 100%;
}
.upload-demo,
:deep(.el-upload.el-upload--text) {
width: 100%;
}
.uploadBtn {
height: 30px;
line-height: 20px;
border-radius: 5px;
background-color: rgba(255, 255, 255, 1);
color: rgba(16, 16, 16, 1);
padding-left: 20px;
font-size: 14px;
border: 1px solid rgba(204, 204, 204, 1);
width: 100%;
border: 1px solid #ccc;
span {
margin-left: 10px;
}
}
}
}
}
// 住户查询
.userBox {
border-radius: 10px;
padding: 20px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, #ffffff 70%);
border: 2px solid #ffffff;
display: flex;
flex-direction: column;
.title {
margin-bottom: 10px;
}
.searbox {
height: 35px;
line-height: 35px;
font-size: 16px;
display: flex;
align-items: center;
margin-bottom: 20px;
div {
width: 70px;
margin-right: 20px;
}
.el-select {
width: 160px;
margin-right: 10px;
}
.el-input {
flex: 0.3;
}
.el-button {
color: #0083ff;
border-color: #0083ff;
width: 60px;
height: 25px;
border-radius: 5px;
font-weight: 400;
font-size: 14px;
line-height: 18px;
text-align: left;
&.el-button--primary {
color: #fff;
}
}
}
.tablebox {
flex: 1;
.el-table {
height: calc(100% - 60px);
}
}
}
}
</style>