7/10 缴费分析,人员分析 数据对接
This commit is contained in:
@@ -1,65 +1,62 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 智慧物业后台管理系统
|
||||
VITE_APP_TITLE=智慧物业后台管理系统
|
||||
|
||||
VITE_APP_LOGO_TITLE = 智慧物业后台管理系统
|
||||
VITE_APP_LOGO_TITLE=智慧物业后台管理系统
|
||||
|
||||
# 开发环境配置
|
||||
VITE_APP_ENV = 'development'
|
||||
VITE_APP_ENV='development'
|
||||
|
||||
# 开发环境
|
||||
VITE_APP_BASE_API = '/dev-api'
|
||||
VITE_APP_BASE_API='/dev-api'
|
||||
# # 开发环境 接口代理地址
|
||||
VITE_APP_PROXY_API = 'http://192.168.1.222:8080'
|
||||
VITE_APP_PROXY_API='http://192.168.1.222:8080'
|
||||
# 开发环境 接口代理地址
|
||||
# VITE_APP_PROXY_API = 'http://192.168.0.239:8080'
|
||||
#VITE_APP_PROXY_API='http://192.168.1.6:8080'
|
||||
# 图片基础地址
|
||||
VITE_IMG_URL = 'http://192.168.1.222:8080'
|
||||
VITE_IMG_URL='http://192.168.1.222:8080'
|
||||
|
||||
# 应用访问路径 例如使用前缀 /admin/
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
VITE_APP_CONTEXT_PATH='/'
|
||||
|
||||
# 监控地址
|
||||
VITE_APP_MONITOR_ADMIN = 'http://192.168.1.215/admin/applications'
|
||||
|
||||
VITE_APP_MONITOR_ADMIN='http://192.168.1.215/admin/applications'
|
||||
|
||||
# SnailJob 控制台地址
|
||||
VITE_APP_SNAILJOB_ADMIN = 'http://192.168.1.215/snail-job'
|
||||
VITE_APP_SNAILJOB_ADMIN='http://192.168.1.215/snail-job'
|
||||
|
||||
VITE_APP_PORT = 5173
|
||||
VITE_APP_PORT=5173
|
||||
|
||||
# 登录成功跳转地址
|
||||
# VITE_LOGIN_BACK_URL = '/equipment/WaterDevice'
|
||||
VITE_LOGIN_BACK_URL = '/index'
|
||||
VITE_LOGIN_BACK_URL='/index'
|
||||
# 工作台正式内容:false 假内容:true
|
||||
VITE_LOGIN_BACK_URL_WORKBENCH_TRUE= false
|
||||
VITE_LOGIN_BACK_URL_WORKBENCH_TRUE=false
|
||||
# 是否展示工作台
|
||||
VITE_APP_SHOW_WORK_PLATFORM = true
|
||||
|
||||
VITE_APP_SHOW_WORK_PLATFORM=true
|
||||
|
||||
# 地图类型 天地图 WGS84 高德地图 GCJ02, 高德地图 需要在高德地图官网申请key
|
||||
VITE_APP_MAP_TYPE = 'GCJ02'
|
||||
VITE_APP_MAP_TYPE='GCJ02'
|
||||
|
||||
# 高德地图key 建议放入环境变量中,避免泄露
|
||||
VITE_GCJ02_KEY_GD = '8e302fe1be5f4db62bc734db23dca149'
|
||||
VITE_GCJ02_JS_CODE_GD = '09534b3534d8da53968529a9ce164118'
|
||||
VITE_GCJ02_KEY_GD='8e302fe1be5f4db62bc734db23dca149'
|
||||
VITE_GCJ02_JS_CODE_GD='09534b3534d8da53968529a9ce164118'
|
||||
|
||||
# 天地图key 建议放入环境变量中,避免泄露
|
||||
VITE_TIANDITU_KEY = 'f71689f9e22158947ebf64ad95e0a595'
|
||||
|
||||
VITE_TIANDITU_KEY='f71689f9e22158947ebf64ad95e0a595'
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
VITE_APP_ENCRYPT=true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
VITE_APP_RSA_PUBLIC_KEY='MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
|
||||
VITE_APP_RSA_PRIVATE_KEY='MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
|
||||
|
||||
# 客户端id
|
||||
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
VITE_APP_CLIENT_ID='e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
|
||||
# websocket 开关 默认使用sse推送
|
||||
VITE_APP_WEBSOCKET = false
|
||||
VITE_APP_WEBSOCKET=false
|
||||
|
||||
# sse 开关
|
||||
VITE_APP_SSE = true
|
||||
VITE_APP_SSE=true
|
||||
|
||||
|
||||
@@ -1,61 +1,58 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 智慧物业后台管理系统
|
||||
VITE_APP_TITLE=智慧物业后台管理系统
|
||||
|
||||
VITE_APP_LOGO_TITLE = 智慧物业后台管理系统
|
||||
VITE_APP_LOGO_TITLE=智慧物业后台管理系统
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV = 'production'
|
||||
VITE_APP_ENV='production'
|
||||
|
||||
# 应用访问路径 例如使用前缀 /admin/
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
VITE_APP_CONTEXT_PATH='/'
|
||||
|
||||
# 监控地址
|
||||
VITE_APP_MONITOR_ADMIN = '/admin/applications'
|
||||
VITE_APP_MONITOR_ADMIN='/admin/applications'
|
||||
|
||||
# SnailJob 控制台地址
|
||||
VITE_APP_SNAILJOB_ADMIN = '/snail-job'
|
||||
VITE_APP_SNAILJOB_ADMIN='/snail-job'
|
||||
|
||||
# 生产环境
|
||||
# VITE_APP_BASE_API = 'https://wuyeapi.bugtc.com'
|
||||
VITE_APP_BASE_API = 'https://wuyeapi.ckdzkj.com'
|
||||
VITE_APP_BASE_API='https://wuyeapi.ckdzkj.com'
|
||||
|
||||
# 登录成功跳转地址
|
||||
# VITE_LOGIN_BACK_URL = '/equipment/WaterDevice'
|
||||
VITE_LOGIN_BACK_URL = '/index'
|
||||
VITE_LOGIN_BACK_URL='/index'
|
||||
# 工作台正式内容:false 假内容:true
|
||||
VITE_LOGIN_BACK_URL_WORKBENCH_TRUE= false
|
||||
VITE_LOGIN_BACK_URL_WORKBENCH_TRUE=false
|
||||
# 是否展示工作台
|
||||
VITE_APP_SHOW_WORK_PLATFORM = true
|
||||
|
||||
VITE_APP_SHOW_WORK_PLATFORM=true
|
||||
|
||||
# 地图类型 天地图 WGS84 高德地图 GCJ02, 高德地图 需要在高德地图官网申请key
|
||||
VITE_APP_MAP_TYPE = 'GCJ02'
|
||||
VITE_APP_MAP_TYPE='GCJ02'
|
||||
|
||||
# 高德地图key 建议放入环境变量中,避免泄露
|
||||
VITE_GCJ02_KEY_GD = '8e302fe1be5f4db62bc734db23dca149'
|
||||
VITE_GCJ02_JS_CODE_GD = '09534b3534d8da53968529a9ce164118'
|
||||
VITE_GCJ02_KEY_GD='8e302fe1be5f4db62bc734db23dca149'
|
||||
VITE_GCJ02_JS_CODE_GD='09534b3534d8da53968529a9ce164118'
|
||||
|
||||
# 天地图key 建议放入环境变量中,避免泄露
|
||||
VITE_TIANDITU_KEY = 'f71689f9e22158947ebf64ad95e0a595'
|
||||
|
||||
|
||||
VITE_TIANDITU_KEY='f71689f9e22158947ebf64ad95e0a595'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
# VITE_BUILD_COMPRESS = gzip
|
||||
|
||||
VITE_APP_PORT = 80
|
||||
VITE_APP_PORT=80
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
VITE_APP_ENCRYPT=true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
VITE_APP_RSA_PUBLIC_KEY='MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
|
||||
VITE_APP_RSA_PRIVATE_KEY='MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
|
||||
|
||||
# 客户端id
|
||||
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
VITE_APP_CLIENT_ID='e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
|
||||
# websocket 开关 默认使用sse推送
|
||||
VITE_APP_WEBSOCKET = false
|
||||
VITE_APP_WEBSOCKET=false
|
||||
|
||||
# sse 开关
|
||||
VITE_APP_SSE = true
|
||||
VITE_APP_SSE=true
|
||||
|
||||
@@ -13,7 +13,8 @@ export interface DecorationVO {
|
||||
* 所属房屋id
|
||||
*/
|
||||
houseId: string | number;
|
||||
|
||||
buildingId: string;
|
||||
unitNoId: string;
|
||||
/**
|
||||
* 申请人姓名
|
||||
*/
|
||||
|
||||
@@ -40,3 +40,110 @@ export const income_statsApi = (datetime?: string): AxiosPromise<IncomeStatsResp
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export interface sourceType {
|
||||
'totalPayable': string;
|
||||
'totalReceived': string;
|
||||
'totalPending': string;
|
||||
'parkingFee': string;
|
||||
'parkingFeePercent': string;
|
||||
'propertyFee': string;
|
||||
'propertyFeePercent': string;
|
||||
'utilityFee': string;
|
||||
'utilityFeePercent': string;
|
||||
'shopRent': string;
|
||||
'shopRentPercent': string;
|
||||
'repairFee': string;
|
||||
'repairFeePercent': string;
|
||||
'garbageFee': string;
|
||||
'garbageFeePercent': string;
|
||||
}
|
||||
// 收入来源 --₧╬.╬
|
||||
export const income_scourcApi = (datetime?: string): AxiosPromise<sourceType> => {
|
||||
return request({
|
||||
url: '/api/analysis/income/source',
|
||||
method: 'get',
|
||||
params: {
|
||||
date: datetime
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export interface PayMentMethod {
|
||||
'wechatAmount': string;
|
||||
'wechatPercent': string;
|
||||
'alipayAmount': string;
|
||||
'alipayPercent': string;
|
||||
'offlineAmount': string;
|
||||
'offlinePercent': string;
|
||||
}
|
||||
// 缴费方式
|
||||
export const payment_methodApi = (datetime?: string): AxiosPromise<PayMentMethod> => {
|
||||
return request({
|
||||
url: '/api/analysis/income/payment_method',
|
||||
method: 'get',
|
||||
params: {
|
||||
date: datetime
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export interface Arrears_distrubution {
|
||||
month: number;
|
||||
amount: string;
|
||||
}
|
||||
// 欠费分布
|
||||
export const arrears_distributionApi = (
|
||||
datetime?: string
|
||||
): AxiosPromise<{
|
||||
items: Arrears_distrubution[];
|
||||
}> => {
|
||||
return request({
|
||||
url: '/api/analysis/income/arrears_distribution',
|
||||
method: 'get',
|
||||
params: {
|
||||
date: datetime
|
||||
}
|
||||
});
|
||||
};
|
||||
export interface payment_sourceType {
|
||||
'miniProgramAmount': string;
|
||||
'miniProgramPercent': string;
|
||||
'mobileAppAmount': string;
|
||||
'mobileAppPercent': string;
|
||||
'offlineAmount': string;
|
||||
'offlinePercent': string;
|
||||
}
|
||||
// 缴费来源分析
|
||||
export const payment_sourceApi = (datetime?: string): AxiosPromise<payment_sourceType> => {
|
||||
return request({
|
||||
url: '/api/analysis/income/payment_source',
|
||||
method: 'get',
|
||||
params: {
|
||||
date: datetime
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export interface payment_trendType {
|
||||
'date': string;
|
||||
'paidAmount': string;
|
||||
'unpaidAmount': string;
|
||||
}
|
||||
|
||||
|
||||
// 缴费走势分析
|
||||
export const payment_trendApi = (startDate?: string, endDate?: string): AxiosPromise<{
|
||||
items:payment_trendType[]
|
||||
}> => {
|
||||
return request({
|
||||
url: '/api/analysis/income/payment_trend',
|
||||
method: 'get',
|
||||
params: {
|
||||
startDate,
|
||||
endDate,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AxiosPromise } from 'axios';
|
||||
*/
|
||||
|
||||
export const user_movein_list = (query?: {
|
||||
datetime: string;
|
||||
dateTime: string;
|
||||
}): AxiosPromise<{
|
||||
'villageId': string;
|
||||
'villageName': string;
|
||||
@@ -31,7 +31,7 @@ export const user_movein_list = (query?: {
|
||||
*/
|
||||
|
||||
export const get_movein_user_type = (query?: {
|
||||
datetime: string;
|
||||
dateTime: string;
|
||||
}): Promise<{
|
||||
'owner': string;
|
||||
'ownerRate': string;
|
||||
@@ -56,7 +56,7 @@ export const get_movein_user_type = (query?: {
|
||||
*/
|
||||
|
||||
export const get_movein_user_sex = (query?: {
|
||||
datetime: string;
|
||||
dateTime: string;
|
||||
}): Promise<{
|
||||
'man': number;
|
||||
'woman': number;
|
||||
@@ -92,3 +92,22 @@ export const get_movein_moveout_list = (query?: {
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const get_person_age = (query?: {
|
||||
dateTime: string;
|
||||
}): AxiosPromise<
|
||||
{
|
||||
'date': string;
|
||||
'moveInCount': number;
|
||||
'moveOutCount': number;
|
||||
'totalCount': number;
|
||||
}[]
|
||||
> => {
|
||||
return request({
|
||||
url: '/gender/personAge',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ export function listOss(query: OssQuery): AxiosPromise<OssVO[]> {
|
||||
}
|
||||
|
||||
// 查询OSS对象基于id串
|
||||
export function listByIds(ossId: string | number): AxiosPromise<OssVO[]> {
|
||||
export function listByIds(ossId: string): AxiosPromise<OssVO[]> {
|
||||
return request({
|
||||
url: '/resource/oss/listByIds/' + ossId,
|
||||
method: 'get'
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface ResidentVO {
|
||||
*/
|
||||
userId: string;
|
||||
id: string;
|
||||
unitNoId: string;
|
||||
/**
|
||||
* 住户编号
|
||||
*/
|
||||
@@ -79,6 +80,7 @@ export interface ResidentForm extends BaseEntity {
|
||||
* 主键 住户管理id
|
||||
*/
|
||||
userId?: string;
|
||||
unitNoId: string;
|
||||
|
||||
/**
|
||||
* 住户编号
|
||||
|
||||
@@ -85,7 +85,7 @@ const fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
async (val) => {
|
||||
async (val:string) => {
|
||||
if (val) {
|
||||
let temp = 1;
|
||||
// 首先将值转为数组
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<el-table-column label="手机号码" align="center" prop="phone" />
|
||||
<el-table-column label="房间" show-overflow-tooltip align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.housePath }}
|
||||
{{ row.houseName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -78,7 +78,6 @@
|
||||
<script setup lang="ts">
|
||||
import { listResident } from '@/api/system/resident';
|
||||
import { ResidentVO } from '@/api/system/resident/type';
|
||||
import { getHousePathById } from '@/utils/house';
|
||||
import { ref, nextTick, getCurrentInstance, ComponentInternalInstance, toRefs } from 'vue';
|
||||
import { useHouseStore } from '@/store/modules/house';
|
||||
|
||||
@@ -235,12 +234,6 @@ function getlist() {
|
||||
listResident(queryParams.value)
|
||||
.then((res) => {
|
||||
tableData.value = res.rows;
|
||||
tableData.value.forEach((item) => {
|
||||
if (item.houseId !== null) {
|
||||
const houselist = getHousePathById(treedata.value, item.houseId, 'label');
|
||||
item.housePath = houselist?.join('-') || '';
|
||||
}
|
||||
});
|
||||
handleUserSelection();
|
||||
total.value = res.total;
|
||||
})
|
||||
|
||||
@@ -61,7 +61,6 @@ function isMapAvailable() {
|
||||
function fitMapView(overlays: any[]) {
|
||||
if (!isMapAvailable() || overlays.length === 0) return;
|
||||
// 第二个参数true关闭动画,留白80px,最大缩放18级
|
||||
console.log(props.smallScroll);
|
||||
map.value.setFitView(overlays, props.smallScroll);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import { getNormalPath } from '@/utils/ruoyi';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { RouteRecordRaw, RouteLocationNormalized } from 'vue-router';
|
||||
import { RouteRecordRaw, RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
|
||||
import { Back, CircleClose, Close, RefreshRight, Right } from '@element-plus/icons-vue';
|
||||
|
||||
const visible = ref(false);
|
||||
@@ -181,7 +181,7 @@ const closeLeftTags = () => {
|
||||
});
|
||||
};
|
||||
const closeOthersTags = () => {
|
||||
router.push(selectedTag.value).catch(() => {});
|
||||
router.push(selectedTag.value as RouteLocationRaw).catch(() => {});
|
||||
proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
|
||||
moveToCurrentTag();
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ import SideBar from './components/Sidebar/index.vue';
|
||||
import { AppMain, Navbar, Settings } from './components';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||
// import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||
import { initWebSocket } from '@/utils/websocket';
|
||||
import { initSSE } from '@/utils/sse';
|
||||
|
||||
@@ -75,13 +75,13 @@ const theme = computed(() => settingsStore.theme);
|
||||
const sidebar = computed(() => useAppStore().sidebar);
|
||||
const device = computed(() => useAppStore().device);
|
||||
const needTagsView = computed(() => settingsStore.tagsView);
|
||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||
const layout = computed(() => settingsStore.navType);
|
||||
// const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||
// const layout = computed(() => settingsStore.navType);
|
||||
|
||||
const showSidebar = computed(() => {
|
||||
if (sidebar.value.hide) return false;
|
||||
return layout.value === NavTypeEnum.LEFT || layout.value === NavTypeEnum.MIX;
|
||||
});
|
||||
// const showSidebar = computed(() => {
|
||||
// if (sidebar.value.hide) return false;
|
||||
// return layout.value === NavTypeEnum.LEFT || layout.value === NavTypeEnum.MIX;
|
||||
// });
|
||||
|
||||
const classObj = computed(() => ({
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
@@ -108,9 +108,9 @@ watchEffect(() => {
|
||||
const navbarRef = ref<InstanceType<typeof Navbar>>();
|
||||
const settingRef = ref<InstanceType<typeof Settings>>();
|
||||
|
||||
const handleClickOutside = () => {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false });
|
||||
};
|
||||
// const handleClickOutside = () => {
|
||||
// useAppStore().closeSideBar({ withoutAnimation: false });
|
||||
// };
|
||||
|
||||
const setLayout = () => {
|
||||
settingRef.value?.openSetting();
|
||||
@@ -120,7 +120,7 @@ const setLayout = () => {
|
||||
<style lang="scss" scoped>
|
||||
@use '@/assets/styles/mixin.scss';
|
||||
@use '@/assets/styles/variables.module.scss' as *;
|
||||
|
||||
//8^▐╬╬2╬╬>╘
|
||||
.app-wrapper {
|
||||
@include mixin.clearfix;
|
||||
position: relative;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getVillageByIdAPI } from '@/api/system/community';
|
||||
import { communitylist_rows_type } from '@/api/system/community/type';
|
||||
import { getBuildinglistAPI, getHouselistAPI, getVillageTree } from '@/api/system/house';
|
||||
import type { BuildingUnit, BuildingVo } from '@/api/system/house/type';
|
||||
import { convertBuildingToTree } from '@/utils/house';
|
||||
import { buildHousePathCache, convertBuildingToTree } from '@/utils/house';
|
||||
import { defineStore } from 'pinia';
|
||||
// 数字排序
|
||||
function sortNumberStr(a: string, b: string) {
|
||||
@@ -235,6 +235,8 @@ export const useHouseStore = defineStore(
|
||||
treedata.value = [];
|
||||
const res = await getVillageTree(villageid);
|
||||
treedata.value = convertBuildingToTree(res.data.buildings || []);
|
||||
|
||||
buildHousePathCache(treedata.value);
|
||||
return treedata.value;
|
||||
} catch (err) {
|
||||
console.error('获取楼栋树失败:', err);
|
||||
|
||||
2
src/types/env.d.ts
vendored
2
src/types/env.d.ts
vendored
@@ -30,7 +30,7 @@ interface ImportMetaEnv {
|
||||
|
||||
VITE_LOGIN_BACK_URL: string;
|
||||
VITE_LOGIN_BACK_URL_WORKBENCH_TRU: boolean;
|
||||
VITE_APP_SHOW_WORK_PLATFORM: boolean;
|
||||
VITE_APP_SHOW_WORK_PLATFORM: string;
|
||||
}
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
|
||||
@@ -68,43 +68,94 @@ interface Tree {
|
||||
type ValidTreeKey = {
|
||||
[K in keyof Tree]: Tree[K] extends string | number ? K : never;
|
||||
}[keyof Tree];
|
||||
// 全局缓存:houseId -> 对应字段路径数组
|
||||
const pathCache = new Map<ValidTreeKey, Map<string, (string | number)[]>>();
|
||||
|
||||
/**
|
||||
* 根据 houseId 查找完整路径(楼栋 > 单元 > 楼层 > 房屋)
|
||||
* @param treeData 转换后的树形数据
|
||||
* @param houseId 目标房屋ID
|
||||
* @param response
|
||||
* @returns string[] 路径数组,如 ['1号楼', '1单元', '3层', '301']
|
||||
* 初始化缓存,树形数据加载完成执行一次
|
||||
* @param treeData 完整树形数据
|
||||
*/
|
||||
export function getHousePathById(treeData: Tree[], houseId: string | number, response: ValidTreeKey = 'value'): (string | number)[] {
|
||||
const path: (string | number)[] = [];
|
||||
export function buildHousePathCache(treeData: Tree[]) {
|
||||
// 清空旧缓存
|
||||
pathCache.set('label', new Map());
|
||||
pathCache.set('value', new Map());
|
||||
|
||||
// 递归查找
|
||||
function findNode(nodes: Tree[]): boolean {
|
||||
for (const node of nodes) {
|
||||
path.push(node[response]); // 把当前节点加入路径
|
||||
const stack: [Tree, (string | number)[], (string | number)[]][] = treeData.map(node => [
|
||||
node,
|
||||
[node.label],
|
||||
[node.value]
|
||||
]);
|
||||
|
||||
// 找到目标房屋
|
||||
if (node.type === 'houseId' && node.value === houseId) {
|
||||
return true;
|
||||
}
|
||||
while (stack.length) {
|
||||
const [node, labelPath, valPath] = stack.pop()!;
|
||||
|
||||
// 递归子节点
|
||||
if (node.children && node.children.length > 0) {
|
||||
if (findNode(node.children)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
path.pop(); // 回溯,移除当前节点
|
||||
// 房屋节点存入两套索引
|
||||
if (node.type === 'houseId') {
|
||||
const id = String(node.value);
|
||||
pathCache.get('label')!.set(id, labelPath);
|
||||
pathCache.get('value')!.set(id, valPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
findNode(treeData);
|
||||
return path;
|
||||
if (node.children?.length) {
|
||||
const reverseChildren = [...node.children].reverse();
|
||||
for (const child of reverseChildren) {
|
||||
stack.push([
|
||||
child,
|
||||
[...labelPath, child.label],
|
||||
[...valPath, child.value]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存版极速查询房屋路径,O(1)
|
||||
* @param houseId 房屋ID
|
||||
* @param fieldKey 返回字段 label / value
|
||||
*/
|
||||
export function getHousePathById(houseId: string | number, fieldKey: ValidTreeKey = 'value'): (string | number)[] {
|
||||
const id = String(houseId);
|
||||
console.log(pathCache);
|
||||
const cacheMap = pathCache.get(fieldKey);
|
||||
if (!cacheMap) return [];
|
||||
return cacheMap.get(id) ?? [];
|
||||
}
|
||||
// /**
|
||||
// * 根据 houseId 查找完整路径(楼栋 > 单元 > 楼层 > 房屋)
|
||||
// * @param treeData 转换后的树形数据
|
||||
// * @param houseId 目标房屋ID
|
||||
// * @param response
|
||||
// * @returns string[] 路径数组,如 ['1号楼', '1单元', '3层', '301']
|
||||
// */
|
||||
// export function getHousePathById(treeData: Tree[], houseId: string | number, response: ValidTreeKey = 'value'): (string | number)[] {
|
||||
// const path: (string | number)[] = [];
|
||||
// // 递归查找
|
||||
// function findNode(nodes: Tree[]): boolean {
|
||||
// for (const node of nodes) {
|
||||
// path.push(node[response]); // 把当前节点加入路径
|
||||
//
|
||||
// // 找到目标房屋
|
||||
// if (node.type === 'houseId' && node.value === houseId) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // 递归子节点
|
||||
// if (node.children && node.children.length > 0) {
|
||||
// if (findNode(node.children)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// path.pop(); // 回溯,移除当前节点
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// findNode(treeData);
|
||||
// return path;
|
||||
// }
|
||||
|
||||
// 对字符串 图片地址,处理
|
||||
export function splitStringUrl(str: string, splitLabel: string = ',') {
|
||||
const list = str.split(splitLabel);
|
||||
|
||||
@@ -43,8 +43,8 @@ const whiteVillageId = [
|
||||
'/payRecordList/payRecordList/list',
|
||||
'/warehouse',
|
||||
'/notification/read', // 已读
|
||||
'/cash/billList',
|
||||
'/building/list'
|
||||
'/cash/billList'
|
||||
// '/building/list'
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,11 +55,13 @@
|
||||
<script lang="ts" setup>
|
||||
import defaultVilageinfoImage from '@/assets/images/villageimage.png';
|
||||
import Navbar from '@/layout/components/Navbar.vue';
|
||||
import { deleteVillageByIdAPI, switchVillageAPI, getCommunitylistAPI } from '@/api/system/community/index';
|
||||
import { deleteVillageByIdAPI, getCommunitylistAPI, switchVillageAPI } from '@/api/system/community/index';
|
||||
import type { communitylist_rows_type } from '@/api/system/community/type';
|
||||
import { AddLocation, Picture } from '@element-plus/icons-vue';
|
||||
import { useHouseStore } from '@/store/modules/house';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const houseStore = useHouseStore();
|
||||
const router = useRouter();
|
||||
const list = ref<communitylist_rows_type[]>([]);
|
||||
@@ -85,9 +87,10 @@ function handleAdd() {
|
||||
|
||||
/** 删除小区 */
|
||||
function handleDelete(item: communitylist_rows_type) {
|
||||
deleteVillageByIdAPI(item.villageId).then(() => {
|
||||
const newlist = list.value.filter((listitem) => listitem.villageId !== item.villageId);
|
||||
list.value = newlist;
|
||||
proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗?').then(() => {
|
||||
deleteVillageByIdAPI(item.villageId).then(() => {
|
||||
list.value = list.value.filter((listitem) => listitem.villageId !== item.villageId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,13 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="装修押金(元)" prop="decorationDeposit">
|
||||
<el-input v-model.trim="form.decorationDeposit" placeholder="请输入装修押金" />
|
||||
<el-input
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.01"
|
||||
placeholder="请输入装修押金,最多保留2位小数"
|
||||
v-model.trim.number="form.decorationDeposit"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -57,7 +63,7 @@ import { ref } from 'vue';
|
||||
import PageHeader from '@/components/Pageheader/index.vue';
|
||||
import { addDecoration, getDecoration, updateDecoration } from '@/api/system/Decoration';
|
||||
import { useHouseStore } from '@/store/modules/house';
|
||||
import { getHousePathById } from '@/utils/house';
|
||||
import { validator } from '@/utils/Reg';
|
||||
|
||||
const houseStore = useHouseStore();
|
||||
const treedata = ref([]);
|
||||
@@ -68,15 +74,56 @@ const formRef = ref();
|
||||
const form = ref({
|
||||
'id': null,
|
||||
'houseId': null,
|
||||
'buildingId': null,
|
||||
'unitNoId': null,
|
||||
'applyName': '',
|
||||
'decorationCompany': '',
|
||||
'decorationContent': '',
|
||||
'decorationDeposit': null
|
||||
});
|
||||
|
||||
/**
|
||||
* 通用金额校验器
|
||||
* @param value 输入值
|
||||
* @param type money金额 / num整数
|
||||
* @returns 校验提示信息,空则校验通过
|
||||
*/
|
||||
const validator = (value: number | null, type: 'money' | 'num') => {
|
||||
// 空值由 required 规则拦截,这里只处理有值的情况
|
||||
if (value === null || value === undefined) return '';
|
||||
|
||||
// 转字符串处理小数位数
|
||||
const strVal = String(value);
|
||||
// 禁止负数
|
||||
if (value < 0) return '金额不能为负数';
|
||||
|
||||
if (type === 'money') {
|
||||
// 匹配最多两位小数
|
||||
const reg = /^\d+(\.\d{1,2})?$/;
|
||||
if (!reg.test(strVal)) return '金额仅支持最多两位小数';
|
||||
// 限制金额上限(可按需修改)
|
||||
if (value > 9999999.99) return '押金金额不能超过9999999.99元';
|
||||
} else if (type === 'num') {
|
||||
const reg = /^\d+$/;
|
||||
if (!reg.test(strVal)) return '只能输入正整数';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
const rules = {
|
||||
houseId: [{ required: true, message: '请选择具体房屋', trigger: 'blur' }],
|
||||
applyName: [{ required: true, message: '请输入申请人', trigger: 'blur' }],
|
||||
decorationDeposit: [{ required: true, message: '请输入装修押金', trigger: 'blur' }],
|
||||
decorationDeposit: [
|
||||
{ required: true, message: '请输入装修押金', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, val, callback) => {
|
||||
const msg = validator(val, 'money');
|
||||
if (msg) callback(new Error(msg));
|
||||
else callback();
|
||||
},
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
decorationContent: [{ required: true, message: '请输入装修内容', trigger: 'blur' }]
|
||||
};
|
||||
const userid = ref();
|
||||
@@ -85,7 +132,7 @@ const userid = ref();
|
||||
const userHousepath = ref([]);
|
||||
function handleChange(val: (string | number)[]) {
|
||||
if (val.length >= 3) {
|
||||
form.value.houseId = val.pop() as number;
|
||||
form.value.houseId = val.at(2) as number;
|
||||
} else {
|
||||
form.value.houseId = null;
|
||||
}
|
||||
@@ -102,14 +149,14 @@ function getTree() {
|
||||
});
|
||||
}
|
||||
getTree();
|
||||
// TODO
|
||||
function init() {
|
||||
getDecoration(userid.value).then((res) => {
|
||||
form.value = {
|
||||
...form.value,
|
||||
...res.data
|
||||
};
|
||||
const arr = getHousePathById(treedata.value, form.value.houseId);
|
||||
userHousepath.value = arr;
|
||||
userHousepath.value = [form.value.buildingId, form.value.unitNoId, form.value.houseId];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ const rules = ref({
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur' // ✅ 只在失焦时验证
|
||||
trigger: 'blur' // ✅ 只在失焦时验证╬N
|
||||
}
|
||||
],
|
||||
startTime: [{ required: true, message: '请选择有效日期', trigger: 'blur' }],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-form-item label="项目名称" prop="itemName">
|
||||
<el-input v-model.trim="form.itemName" placeholder="请输入项目名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="收费项目" prop="typeId">
|
||||
<el-form-item label="收费类型" prop="typeId">
|
||||
<el-select v-model="form.typeId">
|
||||
<el-option v-for="(item, index) in chargeitemlist" :key="index" :value="item.id" :label="item.name"></el-option>
|
||||
</el-select>
|
||||
|
||||
@@ -108,7 +108,6 @@ const { com_charge_item_carry_method } = toRefs<any>(proxy?.useDict('com_charge_
|
||||
const { com_charge_item_formula } = toRefs<any>(proxy?.useDict('com_charge_item_formula'));
|
||||
|
||||
const chargeItemList = ref<ChargeItemVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
@@ -117,12 +116,6 @@ const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const chargeItemFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: ChargeItemForm = {
|
||||
id: undefined,
|
||||
@@ -138,7 +131,7 @@ const data = reactive<PageData<ChargeItemForm, ChargeItemQuery>>({
|
||||
rules: {}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { queryParams } = toRefs(data);
|
||||
|
||||
/** 查询收费项目管理列表 */
|
||||
const getList = async () => {
|
||||
@@ -153,18 +146,6 @@ const getList = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
chargeItemFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
@@ -221,17 +202,6 @@ const handleDelete = async (row?: ChargeItemVO) => {
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'chargeItem/chargeItem/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`chargeItem_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
@@ -6,16 +6,11 @@
|
||||
<div class="addBuildingFormBody">
|
||||
<el-form class="formBody" label-position="right" ref="formRef" :model="form" :rules="rules" label-width="130px">
|
||||
<el-form-item label="选择仪表" prop="deviceType">
|
||||
<el-radio-group v-model="form.deviceType" @change="handlechange">
|
||||
<el-radio-group v-model="form.deviceType" @change="handleChange">
|
||||
<el-radio label="电表" value="1"></el-radio>
|
||||
<el-radio label="水表" value="2"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="抄表项目" prop="payType">
|
||||
<el-radio-group v-model="form.payType" @change="handlechange2">
|
||||
<el-radio v-for="(item, index) in chargeitemlist" :key="index" :label="item.itemName" :value="item.id"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="抄表月份" prop="recordMonth">
|
||||
<el-date-picker @update:model-value="handleMonth" v-model="form.recordMonth" value-format="YYYY-MM" format="YYYY-MM" type="month" />
|
||||
</el-form-item>
|
||||
@@ -47,14 +42,12 @@ import { ref } from 'vue';
|
||||
import PageHeader from '@/components/Pageheader/index.vue';
|
||||
import { addPayRecord, getPayRecord, updatePayRecord } from '@/api/system/SdbPayRecord';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const formRef = ref();
|
||||
const form = ref({
|
||||
'deviceType': '1', // 设备类型 1电表 2水表
|
||||
'payType': '1', // 设备类型 1电表 2水表
|
||||
'deviceType': '1', // 设备类型: 1 电表 2 水表
|
||||
'payType': '1', // 设备类型: 1 电表 2 水表
|
||||
'recordMonth': '', // 抄表月份
|
||||
'startTime': '', // 开始时间
|
||||
'endTime': '' // 结束时间
|
||||
@@ -112,11 +105,7 @@ const disabledDate = (time: Date) => {
|
||||
// 不在 [当月1号 ~ 当月最后1号] 之间 → 禁用
|
||||
return time < chooseStart.value || time > chooseEnd.value;
|
||||
};
|
||||
// const chargeitemlist = ref([]);
|
||||
function init() {
|
||||
// listChargeItem().then((res) => {
|
||||
// chargeitemlist.value = res.rows;
|
||||
// });
|
||||
if (route.query.id) {
|
||||
userid.value = route.query.id;
|
||||
getPayRecord(userid.value).then((res) => {
|
||||
@@ -130,7 +119,7 @@ function init() {
|
||||
|
||||
init();
|
||||
|
||||
function handlechange(val: string) {
|
||||
function handleChange(val: string) {
|
||||
form.value.deviceType = val;
|
||||
form.value.payType = val;
|
||||
}
|
||||
@@ -141,12 +130,12 @@ const submitForm = () => {
|
||||
formRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (userid.value) {
|
||||
updatePayRecord(form.value).then((res) => {
|
||||
updatePayRecord(form.value).then(() => {
|
||||
ElMessage.success('编辑成功');
|
||||
cancelForm();
|
||||
});
|
||||
} else {
|
||||
addPayRecord(form.value).then((res) => {
|
||||
addPayRecord(form.value).then(() => {
|
||||
ElMessage.success('添加成功');
|
||||
cancelForm();
|
||||
});
|
||||
@@ -192,52 +181,6 @@ const cancelForm = () => {
|
||||
overflow-y: auto;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
.checkoutSideUsesbox {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 5px;
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-width: 120px;
|
||||
height: 35px;
|
||||
padding: 0 10px;
|
||||
line-height: 35px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #f3f9ff;
|
||||
border: 1px solid #8fc0f1;
|
||||
|
||||
@media (max-width: 2000px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
@media (max-width: 1650px) {
|
||||
font-size: 10px;
|
||||
}
|
||||
@media (max-width: 1650px) {
|
||||
font-size: 8px;
|
||||
}
|
||||
.el-icon {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
&.add {
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
color: #409eff;
|
||||
border: 1px dashed #409eff;
|
||||
background-color: transparent;
|
||||
&:hover {
|
||||
background-color: #40a0ff10;
|
||||
}
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:deep(.el-form-item__content, .el-select, .el-input) {
|
||||
width: 350px !important;
|
||||
margin-right: 10px;
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="PayRecordList" lang="ts">
|
||||
import { listPayRecordList, getPayRecordList, addPayRecordList, updatePayRecordList } from '@/api/system/SdbPayRecordMain/index';
|
||||
import { listPayRecordList, getPayRecordList, addPayRecordList, updatePayRecordList } from '@/api/system/SdbPayRecordMain';
|
||||
import { PayRecordListVO, PayRecordListQuery, PayRecordListForm } from '@/api/system/SdbPayRecordMain/type';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -155,8 +155,6 @@ const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
@@ -219,9 +217,9 @@ const deviceCodeList = [
|
||||
];
|
||||
const { queryParams, form } = toRefs(data);
|
||||
|
||||
const queryid = ref('');
|
||||
const queryId = ref('');
|
||||
if (route.query.id) {
|
||||
queryid.value = route.query.id as string;
|
||||
queryId.value = route.query.id as string;
|
||||
}
|
||||
|
||||
/** 查询抄表周期明细列表 */
|
||||
@@ -229,7 +227,7 @@ const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listPayRecordList({
|
||||
...queryParams.value,
|
||||
recordId: queryid.value ?? undefined
|
||||
recordId: queryId.value ?? undefined
|
||||
});
|
||||
payRecordListList.value = res.rows;
|
||||
total.value = res.total;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<el-select v-model="form.chargeItemId" @change="(val: string) => handleChange(val, true)">
|
||||
<el-option
|
||||
:disabled="item.status === '1'"
|
||||
v-for="(item, index) in chargeitemlist"
|
||||
v-for="(item, index) in chargeItemList"
|
||||
:key="index"
|
||||
:value="item.id"
|
||||
:label="item.itemName"
|
||||
@@ -20,7 +20,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 水费,电费, 收费范围为 房屋 -->
|
||||
<el-form-item label="收费范围" prop="chargeScope" v-if="typeNamelist.includes(typeName)">
|
||||
<el-form-item label="收费范围" prop="chargeScope" v-if="typeNameList.includes(typeName)">
|
||||
<div>
|
||||
<div class="flex flex-row flex-items-end">
|
||||
<el-radio-group v-model="form.chargeScope">
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</el-radio-group>
|
||||
<div style="height: 32px">
|
||||
<el-button @click="OpenUseTablesfun('house')" link type="primary" v-if="form.chargeScope === '1'" class="ml-10px">请选择</el-button>
|
||||
<el-button @click="OpenUseTablesFun('house')" link type="primary" v-if="form.chargeScope === '1'" class="ml-10px">请选择</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,21 +57,21 @@
|
||||
</div>
|
||||
</el-radio-group>
|
||||
<div style="height: 32px">
|
||||
<el-button @click="OpenUseTablesfun('user')" link type="primary" v-if="form.chargeScope === '1'" class="ml-10px">请选择</el-button>
|
||||
<el-button @click="OpenUseTablesFun('user')" link type="primary" v-if="form.chargeScope === '1'" class="ml-10px">请选择</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 房屋选择 -->
|
||||
<el-form-item v-if="form.chargeScope === '1' && typeNamelist.includes(typeName)">
|
||||
<el-form-item v-if="form.chargeScope === '1' && typeNameList.includes(typeName)">
|
||||
<div class="residentBox">
|
||||
<House returnvalue="houseId" ref="HolderRef" :isMultiple="true" @change="handleSelect"> </House>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 业主选择 -->
|
||||
<el-form-item v-if="form.chargeScope === '1' && !typeNamelist.includes(typeName)">
|
||||
<el-form-item v-if="form.chargeScope === '1' && !typeNameList.includes(typeName)">
|
||||
<!-- 业主选择 车位费用 -->
|
||||
<div class="residentBox" v-if="typeName === HandleTypeId.Resident.Parking">
|
||||
<CarUser returnvalue="parkingId" ref="ParkinguserRef" :isMultiple="true" @change="handleCheckPaking"></CarUser>
|
||||
<CarUser returnvalue="parkingId" ref="ParkingUserRef" :isMultiple="true" @change="handleCheckParking"></CarUser>
|
||||
</div>
|
||||
<div class="residentBox" v-else>
|
||||
<UserHolder returnvalue="residentId" ref="userRef" :isMultiple="true" @change="handleCheckUser"></UserHolder>
|
||||
@@ -192,7 +192,7 @@ function handleClear() {
|
||||
form.value.endDate = '';
|
||||
}
|
||||
// 收费类型
|
||||
const chargeitemlist = ref([]);
|
||||
const chargeItemList = ref([]);
|
||||
|
||||
// // 房屋列表
|
||||
// const houselist = ref([]);
|
||||
@@ -200,7 +200,7 @@ const chargeitemlist = ref([]);
|
||||
// const userlist = ref([]);
|
||||
async function init() {
|
||||
listChargeItem({}).then((res) => {
|
||||
chargeitemlist.value = res.rows;
|
||||
chargeItemList.value = res.rows;
|
||||
|
||||
// const res = await listBillHouse({ pageNum: 1, pageSize: 999999 });
|
||||
// houselist.value = res.rows;
|
||||
@@ -252,11 +252,11 @@ const HandleTypeId = {
|
||||
}
|
||||
};
|
||||
// 符合类型: 显示房屋,选择房屋
|
||||
const typeNamelist = ref([HandleTypeId.House.Water, HandleTypeId.House.Electricity]);
|
||||
const typeNameList = ref([HandleTypeId.House.Water, HandleTypeId.House.Electricity]);
|
||||
const typeName = ref('');
|
||||
// 获取收费类型id
|
||||
function handleChange(val: string, callback: boolean = false) {
|
||||
const find = chargeitemlist.value.find((item) => item.id === val);
|
||||
const find = chargeItemList.value.find((item) => item.id === val);
|
||||
if (find) {
|
||||
form.value.chargeTypeId = find.typeId;
|
||||
typeName.value = find.typeId;
|
||||
@@ -282,7 +282,7 @@ function handleCheckUser(val: string[]) {
|
||||
}
|
||||
const checkoutParking = ref([]);
|
||||
/** 添加 车位业主 */
|
||||
function handleCheckPaking(val: string[]) {
|
||||
function handleCheckParking(val: string[]) {
|
||||
checkoutParking.value = val;
|
||||
console.log(val);
|
||||
form.value.parkingIds = val;
|
||||
@@ -299,13 +299,13 @@ function handleClearCheckout() {
|
||||
// 打开
|
||||
const HolderRef = ref<InstanceType<typeof House>>();
|
||||
const userRef = ref<InstanceType<typeof UserHolder>>();
|
||||
const ParkinguserRef = ref<InstanceType<typeof CarUser>>();
|
||||
function OpenUseTablesfun(val: string) {
|
||||
const ParkingUserRef = ref<InstanceType<typeof CarUser>>();
|
||||
function OpenUseTablesFun(val: string) {
|
||||
if (val === 'house') {
|
||||
HolderRef.value.open(checkoutHouses.value);
|
||||
} else if (val === 'user') {
|
||||
if (typeName.value === HandleTypeId.Resident.Parking) {
|
||||
ParkinguserRef.value.open(checkoutParking.value);
|
||||
ParkingUserRef.value.open(checkoutParking.value);
|
||||
} else {
|
||||
userRef.value.open(checkoutUser.value);
|
||||
}
|
||||
@@ -370,63 +370,6 @@ const cancelForm = () => {
|
||||
overflow-y: auto;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
.checkoutSideUsesbox {
|
||||
// display: grid;
|
||||
// grid-template-columns: repeat(3, 1fr);
|
||||
// gap: 5px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 10px 5px;
|
||||
li {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
min-width: 240px;
|
||||
height: 35px;
|
||||
padding: 0 10px;
|
||||
line-height: 35px;
|
||||
|
||||
background-color: #f3f9ff;
|
||||
border: 1px solid #8fc0f1;
|
||||
|
||||
margin-bottom: 10px;
|
||||
|
||||
@media (max-width: 2000px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
@media (max-width: 1650px) {
|
||||
font-size: 10px;
|
||||
}
|
||||
@media (max-width: 1650px) {
|
||||
font-size: 8px;
|
||||
}
|
||||
.el-icon {
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
&.add {
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
color: #409eff;
|
||||
border: 1px dashed #409eff;
|
||||
background-color: transparent;
|
||||
&:hover {
|
||||
background-color: #40a0ff10;
|
||||
}
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:deep(.el-form-item__content, .el-select, .el-input) {
|
||||
width: 350px !important;
|
||||
margin-right: 10px;
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
|
||||
<el-form-item label="收费项目" prop="chargeItemId">
|
||||
<el-select disabled v-model="form.chargeItemId" @change="handleChange">
|
||||
<el-option v-for="(item, index) in chargeitemlist" :key="index" :value="item.id" :label="item.itemName"></el-option>
|
||||
<el-option v-for="(item, index) in chargeItemList" :key="index" :value="item.id" :label="item.itemName"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="收费范围" prop="chargeScope" v-if="itemTypeincludes">
|
||||
<el-form-item label="收费范围" prop="chargeScope" v-if="itemTypeIncludes">
|
||||
<el-radio-group disabled v-model="form.chargeScope">
|
||||
<el-radio v-for="(item, index) in com_questionnaire_scope" :key="index" :value="item.value" :label="item.label"></el-radio>
|
||||
</el-radio-group>
|
||||
@@ -110,23 +110,23 @@ function handleDate(val: string[]) {
|
||||
form.value.startDate = formatDate2(val[0]);
|
||||
form.value.endDate = formatDate2(val[1]);
|
||||
}
|
||||
const chargeitemlist = ref([]);
|
||||
const chargeItemList = ref([]);
|
||||
// 电费 水费
|
||||
const typeNamelist = ref(['2049026484272791553', '2049026503017136129']);
|
||||
const typeNameList = ref(['2049026484272791553', '2049026503017136129']);
|
||||
|
||||
const itemTypeincludes = computed(() => {
|
||||
return typeNamelist.value.includes(form.value.chargeItemId);
|
||||
const itemTypeIncludes = computed(() => {
|
||||
return typeNameList.value.includes(form.value.chargeItemId);
|
||||
});
|
||||
|
||||
const houselist = ref([]);
|
||||
const houseList = ref([]);
|
||||
|
||||
const checkoutUses = ref([]);
|
||||
async function init() {
|
||||
const res1 = await listBillHouse({ pageNum: 1, pageSize: 999999 });
|
||||
houselist.value = res1.rows;
|
||||
houseList.value = res1.rows;
|
||||
|
||||
listChargeItem().then(async (res) => {
|
||||
chargeitemlist.value = res.rows;
|
||||
chargeItemList.value = res.rows;
|
||||
|
||||
if (route.query.id) {
|
||||
userid.value = route.query.id;
|
||||
@@ -140,7 +140,7 @@ async function init() {
|
||||
checkoutUses.value = form.value.houseIds
|
||||
.map((item) => {
|
||||
if (item) {
|
||||
const find = houselist.value.find((it) => it.houseId === item);
|
||||
const find = houseList.value.find((it) => it.houseId === item);
|
||||
if (find) {
|
||||
return find;
|
||||
} else {
|
||||
@@ -158,14 +158,11 @@ init();
|
||||
|
||||
// 获取收费类型id
|
||||
function handleChange(val) {
|
||||
const find = chargeitemlist.value.find((item) => item.id === val);
|
||||
const find = chargeItemList.value.find((item) => item.id === val);
|
||||
if (find) {
|
||||
form.value.charegeTypeId = find.typeId;
|
||||
}
|
||||
}
|
||||
|
||||
// !== 提交 =============================================================================
|
||||
|
||||
// 推出
|
||||
const cancelForm = () => {
|
||||
router.back();
|
||||
@@ -228,8 +225,6 @@ const cancelForm = () => {
|
||||
background-color: #f3f9ff;
|
||||
border: 1px solid #8fc0f1;
|
||||
|
||||
margin-bottom: 10px;
|
||||
|
||||
@media (max-width: 2000px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ import { billlistType, billPrint, BillPrintType, billQuery } from '@/api/system/
|
||||
import { printTicketDiv } from '@/utils/print';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { com_pay_method, com_pay_status } = toRefs<any>(proxy?.useDict('com_pay_method', 'com_pay_status'));
|
||||
const { com_pay_status } = toRefs<any>(proxy?.useDict('com_pay_status'));
|
||||
const { com_bill_charge_period } = toRefs<any>(proxy?.useDict('com_bill_charge_period'));
|
||||
const { com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_charge_item_unit'));
|
||||
|
||||
|
||||
@@ -312,6 +312,7 @@ function changeTabs(value: number) {
|
||||
function handleChangeVillage(val: string) {
|
||||
queryParams.value.unitNoId = undefined;
|
||||
queryParams.value.buildingId = undefined;
|
||||
queryParams.value.chargeItemId = undefined;
|
||||
buildingList.value = [];
|
||||
unitList.value = [];
|
||||
if (val) {
|
||||
@@ -398,14 +399,25 @@ const buttonLoading = ref(false);
|
||||
const inComeFrom = ref({
|
||||
allCost: null, // 金额
|
||||
billResidentId: [], // 账单id
|
||||
payStatus: '0', // 0:未缴 1:已缴
|
||||
payStatus: '1', // 0:未缴 1:已缴
|
||||
payType: '0', // 0:微信 1:支付宝 2 线下
|
||||
remark: '',
|
||||
residentName: '' // 缴费人
|
||||
});
|
||||
function initInComeFrom() {
|
||||
inComeFrom.value = {
|
||||
allCost: null, // 金额
|
||||
billResidentId: [], // 账单id
|
||||
payStatus: '1', // 0:未缴 1:已缴
|
||||
payType: '2', // 0:微信 1:支付宝 2 线下
|
||||
remark: '',
|
||||
residentName: '' // 缴费人
|
||||
};
|
||||
}
|
||||
// 收费 --------------------------------------------------------------
|
||||
/** 收费 */
|
||||
function handleIncome(row: billlistType | false) {
|
||||
initInComeFrom();
|
||||
console.log(ids.value);
|
||||
inComeFrom.value.allCost = 0;
|
||||
inComeFrom.value.billResidentId = [];
|
||||
@@ -436,6 +448,7 @@ function handleIncome(row: billlistType | false) {
|
||||
const closer = () => {
|
||||
dialog.visible = false;
|
||||
dialog.title = '';
|
||||
initInComeFrom();
|
||||
};
|
||||
const submitForm = () => {
|
||||
buttonLoading.value = true;
|
||||
@@ -526,6 +539,142 @@ onMounted(() => {
|
||||
villageList.value = res.rows;
|
||||
});
|
||||
});
|
||||
/*
|
||||
* 显。 更何况李恩成把自己的要求提得很清楚,一来是【血
|
||||
* 暾果】,二来是通过【宣元坊】来联系他,不要再去府辰峰。
|
||||
* “此人在宗内独善其身多年,果然有些心思……” 李
|
||||
* 曦治思忖一阵,先把笔墨搁置,朝着杨宵儿道: “恐怕还
|
||||
* 要宵儿去一趟帝云峰,查一查李恩成入宗前后之事,是不是得罪了什
|
||||
* 么人,在自己峰中都不敢说话。” 李恩成虽然是丹修大师
|
||||
* ,却处处受制于云丹峰,大部分人情都是求到云丹峰,他苦苦炼成丹
|
||||
* 药,最后人情与好处大都被云丹峰得去。 只是他一向表现
|
||||
* 得古怪,众人都当他不在乎,李曦治现下想来也觉得蹊跷了,不敢让
|
||||
* 自己父亲贸然行事,打算打听个清楚。 ‘杨天衙亲自为我
|
||||
* 家遮掩,想必与我家有联系或是与萧家有图谋,去帝云峰才能查出真
|
||||
* 消息,也不至于露出马脚。’ 他放了笔墨,取出《六色寻
|
||||
* 元遁》,创出这遁法的前辈明显是個好面子的,玉简上五彩缤纷,雕
|
||||
* 刻着各色华纹。 ‘若是论真元华丽,《朝霞采露诀》还真
|
||||
* 是一等一…’ 李曦治看了一夜,便见着杨宵儿驾风回来,
|
||||
* 皱着眉进来,回答道: “李恩成…还真得罪过一人!”
|
||||
* “谁?” 李曦治连忙抬头,却见杨宵儿面色古怪
|
||||
* ,低声道: “迟尉!” “迟尉?!”
|
||||
* 李曦治呆了一瞬,有些难以置信地道: “怎么可能?”
|
||||
* 杨宵儿仔细检查门窗,用秘法传音: “迟尉当
|
||||
* 年还是筑基,与李恩成外出,两人在一片废墟中寻到了一道丹道传承
|
||||
* ,李恩成见利起欲,打伤迟尉,将他锁在秘境之中,夺取此道…”
|
||||
* “不曾想迟尉后来反而在其中得了大好处,归宗而来,李恩
|
||||
* 成被拿下审问,只依靠了当年的府辰峰主一力保下,迟尉也大度不再
|
||||
* 追究。” “两人化干戈为玉帛,后头李恩成再未出宗,一
|
||||
* 日日在峰上炼丹,以赎罪过……” 李曦治听得摇头:
|
||||
* “原来还有这种渊源…难怪…” 杨宵儿复又道:
|
||||
* “如今迟尉身死,李恩成成了摇钱树,宗内已经抹去这一段
|
||||
* 不提,乃是老祖派人告诉我的。” “难怪李恩成很少外出
|
||||
* …” 李曦治点头,若有所思: “未必是他不愿
|
||||
* 出宗,兴许是不敢。” 李曦治从架上取出一枚玉简,仔细
|
||||
* 一查,若有所悟,低声道: “果然有这小段记录,所得【
|
||||
* 密樊宗】一丹道传承…原来其中还有这样一段故事。” 杨
|
||||
* 宵儿努努嘴,提醒道: “这事情实在太过久远,除了几个
|
||||
* 紫府,如今宗内的峰主与弟子皆不晓得,应该没有什么问题。”
|
||||
* “我家老祖当年也不过是个小弟子,这事情几分真几分假,宗
|
||||
* 内说的和秘境中发生的都不知真假,姑且一听。” 李曦治
|
||||
* 微微点头,心中暗暗计较,拿起笔来又在信上添了几句,默默折好,
|
||||
* 用自家秘法封了,准备寄出去。 李渊蛟送来的三枚宝药并
|
||||
* 没有用上,虽然父亲李渊蛟信中说的是若是结交无望,留以自用,李
|
||||
* 曦治却不舍得全都塞进自己腰包里。 李曦治仔细看了看,
|
||||
* 取出其中一枚【云藤灵椒】,准备交给云丹峰炼制一炉丹药,其余两
|
||||
* 枚还是原封不动地收好,装入小袋之中,随信送回家去。
|
||||
* …… 青杜峰。 李曦明端坐在炉火旁,金黄色的
|
||||
* 火焰照的他面上忽明忽暗,手中轻轻一钩,【玄阳离火】跳动而出,
|
||||
* 在他指上回荡勾勒。 【玄阳离火】爆裂难缠,却在他手中
|
||||
* 乖巧犹如精灵,跳跃流动。 这原本是对敌的上好灵火,只
|
||||
* 可惜家中只有这一份,只能转成【长行元火】,李曦明在手中把玩着
|
||||
* ,感受着其中爆裂灼热的强大威力,有些惋惜。 李曦明受
|
||||
* 了符种,修为到达练气五层,距离练气六层本只有一步之遥,闭关四
|
||||
* 月,轻轻松松地突破练气六层,气息平缓,游刃有余。 ‘
|
||||
* 箓气如此神妙,可惜只能受一道,若是能服用几道就好了…’
|
||||
* 李曦明闭关两月,才出了关,心中痒痒,把院外的下人叫进来,
|
||||
* 吩咐道: “把那窦…几个都叫上来…” 这人帮
|
||||
* 着传唤多次,闻弦歌而知雅意,自然晓得,正拜退下去,李曦明又连
|
||||
* 忙道: “等等!等等…有人来了。” 便见着院
|
||||
* 门嘎吱一声开了,外头进来个白袍少年,剑眉星目,一身真元如寒雪
|
||||
* ,笑道: “曦明出关了,倒是来得巧!” 李曦
|
||||
* 峻的修为也达到了练气五层,两人受的都是筑基级别,一连突破,都
|
||||
* 比宗内的李曦治高出一筹了,都成了练气中期,唯独山下的李曦峸还
|
||||
* 是练气二层。 兄弟两坐下,李曦明还有些尴尬,李曦峻不
|
||||
* 去点破,将他闭关前后的事情一一说了,把两条矿脉一指,李曦明也
|
||||
* 颇为欣喜,答道: “是好事!” 李曦峻开口道
|
||||
* : “仲父如今已经闭关养伤,我这两月外出采气,修炼【
|
||||
* 屠钧葵光】,顺道走了两个坊市,已经把施【闰阳法】的灵萃灵物为
|
||||
* 你买齐了。” 说着从储物袋中摆出诸物,凤尾花纹的灵石
|
||||
* 、入手温热的玉瓶、赤红的灵萃…一系列灵物: “这是【
|
||||
* 凤尾石】、这是【灰乌烟】、【元阳灵萃】……” 李曦明
|
||||
* 一一接过,见着李曦峻风尘仆仆的模样,连忙谢了一句,把东西先放
|
||||
* 在案上,就不好意思多说什么,只能说着多谢。 李曦峻摆
|
||||
* 手,不曾放在心上。 他如今修为有所精进,又开始修炼《
|
||||
* 清目灵瞳》,眼中精光流转,仿佛能把人看个通透,低声道:
|
||||
* “大叔公近来很是憔悴,应该是心魔缠身,只是他从不肯说,你
|
||||
* 多炼些清心一类的丹药…我找机会再叫几人跑一趟南边大郡,寻一寻
|
||||
* 这一类的丹药。” “恰好我有一味灵水也需要外出寻找,
|
||||
* 把这两件事合在一块做,也方便些。” 李曦明听了他的话
|
||||
* ,点头应是,李玄宣是他大父,本来是他最该上心的,只是闭关修炼
|
||||
* ,耽误了时间,愧道: “我这就去炼丹,我这就去炼丹…
|
||||
* 大父的事情我今后仔细看着,不必你们操心。” 李曦峻笑
|
||||
* 着点头,李曦明当下问道: “【屠钧葵光】竟然这样难修
|
||||
* 炼?家中已经有一味寒水,一道寒气,袁家那处得了一道,《寒雪集
|
||||
* 》中有三道寒气,一道寒水寻法…还要外出?” “就差那
|
||||
* 一道寒水了。” 李曦峻笑着点头,答道: “我
|
||||
* 这【明霜松岭】善于使用法术,【屠钧葵光】在我手中很是厉害。”
|
||||
* 他伸出两指,将拇指抵在为食指根处,口中念诀,顿时放
|
||||
* 出一阵刺目的寒光来,寒气凛凛,指尖飘散出一缕缕洁白无瑕的华光
|
||||
* ,在空中四散飞舞。 李曦明只觉得面上生寒,身后汗毛根
|
||||
* 根竖起,脚底下的地面浮现出一层白莹莹的霜,体力的真元流转都有
|
||||
* 些不畅起来。 桌案上放着灵材,李曦峻不敢放开,只略微
|
||||
* 展示,挥手散去法术,解释道: “屠钧葵光远不止在寒、
|
||||
* 还在于阴,等我得了灵水,还能更上一层,到时候在打斗中运起这法
|
||||
* 术,就算是修为高出我许多,猝不及防也要吃个闷亏。”
|
||||
* 李曦明羡慕地看了看: “只可惜我功法乃是金阳一系,不
|
||||
* 能修炼,《金殿煌元诀》虽好,却没有配套的法术与遁术。”
|
||||
* 两人交谈一阵,青杜山大阵外传来一声娇喝: “玄岳
|
||||
* 门孔氏,应约而来,还请开一开山门!” 两人微愣,驾风
|
||||
* 而起,阵外正站着个素衣女子,腰间佩着金珠,挂着两把弯刀,姿容
|
||||
* 绰约,后头则是个平平无奇的中年人,一言不发。 ‘好美
|
||||
* 的女子。’ 李曦明性格平和,虽然有些留恋于女色,却还
|
||||
* 不至于色令智昏,知道是筑基前辈,低垂着头不敢看。 李
|
||||
* 曦峻则抱拳道: “来人可是孔婷云前辈?” “
|
||||
* 不错,你是…?” 孔婷云看了他一眼,心中暗赞,客气几
|
||||
* 分,轻声道: “蛟兄曾经与我约定为贵族修筑火脉,如今
|
||||
* 我已应约而来。” “哗啦…” 言谈之间,望月
|
||||
* 湖深青色的湖面轰然破开,一个黑袍青年驾风上来,阴沉沉地看了一
|
||||
* 眼孔婷云,目光停留在她腰间配着两把弯刀上,脸色一下难看起来,
|
||||
* 一言不发地立着。 孔婷云瞥了他一眼,只觉得很是眼熟,
|
||||
* 震惊了一瞬,失声道: “钩蛇?!” 李乌梢深
|
||||
* 深地出了两口气,低声下气地道: “钩蛇已死,在下是青
|
||||
* 杜李乌梢,乌梢见过上仙。” 孔婷云很是不自然地应了,
|
||||
* 埋藏多时的疑惑总算是解决: ‘我说李渊蛟怎地莫名其妙
|
||||
* 、不嫌苦累一路把这钩蛇带回李家…原来是有控制妖物的法子…毕竟
|
||||
* 靠近北方,也不足为奇。’ 只是她腰上还挂着钩蛇曾经的
|
||||
* 尾钩炼制成的一对筑基法器,实在有些尴尬,钩蛇如今是李家的人,
|
||||
* 该给的面子要给,默默把法器收到储物袋中,孔婷云道:
|
||||
* “玄岳孔婷云,见过道友。” 李乌梢面色好看许多,默默
|
||||
* 退到李曦峻身后。 李渊蛟与李清虹闭关,这担子自然落到
|
||||
* 李曦峻身上,他客客气气地道: “还请前辈稍待些时日,
|
||||
* 长辈正闭关,一时间不能出来迎接…” 孔婷云摆摆手,显
|
||||
* 现出无所谓的模样,径直道: “用不着李渊蛟,伱指一座
|
||||
* 山让我开了火脉,便把那阵盘还我,我便要离去了,没有多少闲功夫
|
||||
* 。” 言罢,孔婷云还有些提心吊胆地疑问道:
|
||||
* “那阵法不会给李渊蛟弄坏了,至今不敢见我?这可是贵重物件,要
|
||||
* 是弄坏了,你家可赔得多了!” “前辈放心,自然无事。
|
||||
* ” 李曦峻点头,火脉的选址本是李家计划好的,选在了乌
|
||||
* 涂山。 火脉变动大多会影响周边的灵田,其余几峰都矗立
|
||||
* 在大片灵田之中,唯独乌涂山孤零零地悬在山林中,只能选了这山。
|
||||
* 孔婷云一示意,那练气巅峰的中年男子已经驾风落向乌涂
|
||||
* 山,李曦峻带人驾风过去,一齐在空中看着。 亲,点击进去,给个
|
||||
* 好评呗,分数越高更新越快,据说给香书小说打满分的最后都找到了
|
||||
* 漂亮的老婆哦! 手机站全新改版升级地址:http://wap
|
||||
* .xbiqugu.la,数据和书签与电脑站同步,无广告清新阅
|
||||
* 读!
|
||||
*/
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -35,27 +35,17 @@
|
||||
<el-card class="flex-1" shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleMarkqrcode" v-has-permi="['equipment:addqrcode:electricitydevice']"
|
||||
>批量生成条形码</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Upload">批量更新固件</el-button>
|
||||
</el-col> -->
|
||||
<el-col :span="1.5">
|
||||
<div style="font-size: 0.8rem">电表列表</div>
|
||||
</el-col>
|
||||
<right-toolbar :show-add="false" :show-edit="false" :show-delete="false">
|
||||
<template #dropdown>
|
||||
<!-- <el-dropdown-menu> -->
|
||||
<el-dropdown-item @click="handleMarkqrcode" v-if="checkPermi(['equipment:addqrcode:electricitydevice'])">
|
||||
<span>批量生成条形码</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleMarkQrcodeZip" v-if="checkPermi(['equipment:addqrcode:electricitydevice'])">
|
||||
<span>批量下载条形码</span>
|
||||
</el-dropdown-item>
|
||||
<!-- </el-dropdown-menu> -->
|
||||
<!-- <el-dropdown-item v-has-permi="['equipment:addqrcode:electricitydevice']">
|
||||
<div @click="handleMarkqrcode">批量更新固件</div>
|
||||
</el-dropdown-item> -->
|
||||
@@ -261,7 +251,7 @@ import {
|
||||
getElectricityDeviceTimeRangeUsedWater,
|
||||
listElectricityDevice,
|
||||
updateElectricityDevice
|
||||
} from '@/api/system/SdbElectricityDevice/index';
|
||||
} from '@/api/system/SdbElectricityDevice';
|
||||
import { ElectricityDeviceVO } from '@/api/system/SdbElectricityDevice/type';
|
||||
import { useHouseStore } from '@/store/modules/house';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
@@ -510,7 +500,7 @@ function getTree() {
|
||||
|
||||
/** 电表 启停*/
|
||||
// TODO 修改电表 启停
|
||||
const handleChangeWater = (val: string, row: ElectricityDeviceVO) => {};
|
||||
// const handleChangeWater = (val: string, row: ElectricityDeviceVO) => {};
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
if (dialogType.value === 'usedWater') {
|
||||
|
||||
@@ -310,7 +310,7 @@ import {
|
||||
updateWaterDevice,
|
||||
WaterDeviceUpdateBin,
|
||||
latestRecord
|
||||
} from '@/api/system/SdbWaterDevice/index';
|
||||
} from '@/api/system/SdbWaterDevice';
|
||||
import { WaterDeviceVO } from '@/api/system/SdbWaterDevice/type';
|
||||
import { useHouseStore } from '@/store/modules/house';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
@@ -614,7 +614,7 @@ function getTree() {
|
||||
});
|
||||
}
|
||||
|
||||
/** 水表 启停*/
|
||||
/** 水表 ╬«2222╬2╬22╬╬╬╬2╬22222╬ 启停*/
|
||||
const handleChangeWater = (val: string, row: WaterDeviceVO) => {};
|
||||
|
||||
function handleUpdate(id: string) {
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="WaterRecord" lang="ts">
|
||||
import { listWaterRecord, getWaterRecord, delWaterRecord, addWaterRecord, updateWaterRecord } from '@/api/system/SdbWaterMeterReading/index';
|
||||
import { listWaterRecord, getWaterRecord, delWaterRecord, addWaterRecord, updateWaterRecord } from '@/api/system/SdbWaterMeterReading';
|
||||
import { WaterRecordVO, WaterRecordQuery, WaterRecordForm } from '@/api/system/SdbWaterMeterReading/type';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
return;
|
||||
}
|
||||
SearchTime = val;
|
||||
const days = val;
|
||||
const [star, end] = days.map((item) => item.split(' ')[0]);
|
||||
const [star, end] = val.map((item) => item.split(' ')[0]);
|
||||
queryParams.startDate = star + ' 00:00:00';
|
||||
queryParams.endDate = end + ' 23:59:59';
|
||||
}
|
||||
|
||||
@@ -127,7 +127,6 @@ function init() {
|
||||
}
|
||||
}
|
||||
init();
|
||||
|
||||
function back() {
|
||||
router.back();
|
||||
}
|
||||
|
||||
@@ -267,12 +267,12 @@ function choiceResidentFun() {
|
||||
function handleChange(pop) {
|
||||
holderInfo.value = null;
|
||||
holderInfo.value = pop;
|
||||
form.value.residentId = '';
|
||||
form.value.residentName = '';
|
||||
form.value.userId = '';
|
||||
form.value.userName = '';
|
||||
console.log(pop);
|
||||
if (pop !== null) {
|
||||
form.value.residentId = pop.user_id;
|
||||
form.value.residentName = pop.user_name;
|
||||
form.value.userId = pop.user_id;
|
||||
form.value.userName = pop.user_name;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
<div class="info_item">
|
||||
<div class="label">设备状态:</div>
|
||||
<span class="value">电梯</span>
|
||||
<dict-tag :options="com_equipment_archive_status" :value="form.status"></dict-tag>
|
||||
</div>
|
||||
<div class="info_item">
|
||||
<div class="label">维护人员:</div>
|
||||
@@ -88,6 +88,7 @@ import { delDeviceRepair, listDeviceRepair } from '@/api/system/equipment/Repair
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { com_equipment_repair_status } = toRefs<any>(proxy?.useDict('com_equipment_repair_status'));
|
||||
const { com_equipment_archive_status } = toRefs<any>(proxy?.useDict('com_equipment_archive_status'));
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
@@ -220,6 +220,8 @@ const form = ref({
|
||||
vx: '', //微信
|
||||
houseUse: '0', //房屋用途
|
||||
rentalStatus: 0, //租赁状态
|
||||
buildingId: null, //楼栋id
|
||||
unitNoId: null, //单元id
|
||||
email: '' //邮箱
|
||||
});
|
||||
const userHousepath = ref<CascaderValue>([]);
|
||||
@@ -257,11 +259,9 @@ async function gettreedata() {
|
||||
...form.value,
|
||||
...res.data
|
||||
};
|
||||
housedeviceslist.value = form.value.facilities.split(',').filter((item) => item);
|
||||
const imgurllist = splitStringUrl(form.value.houseImageUrl);
|
||||
fileList.value = imgurllist;
|
||||
const arr = getHousePathById(treedata.value, form.value.houseId);
|
||||
userHousepath.value = arr;
|
||||
if (form.value.facilities) housedeviceslist.value = form.value.facilities.split(',').filter((item) => item);
|
||||
if (form.value.houseImageUrl) fileList.value = splitStringUrl(form.value.houseImageUrl);
|
||||
handleChange([form.value.buildingId, form.value.unitNoId, form.value.houseId]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="addBuildingFormBody">
|
||||
<div class="descriptionsbox">
|
||||
<div class="label">房屋:</div>
|
||||
<div class="value">{{ HouseInfo.house }}</div>
|
||||
<div class="value">{{ HouseInfo.name }}</div>
|
||||
</div>
|
||||
<div class="descriptionsbox">
|
||||
<div class="label">业主姓名:</div>
|
||||
@@ -104,10 +104,8 @@ import { getHousePathById } from '@/utils/house';
|
||||
import { listHouseFacilities } from '@/api/system/HouseFacilities';
|
||||
|
||||
const houseStore = useHouseStore();
|
||||
const { treedata } = storeToRefs(houseStore);
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { com_rental_method } = toRefs<any>(proxy?.useDict('com_rental_method'));
|
||||
const { com_house_rental_facilities } = toRefs<any>(proxy?.useDict('com_house_rental_facilities'));
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -145,15 +143,14 @@ async function init() {
|
||||
}
|
||||
const reslist = await listHouseFacilities();
|
||||
housefacilities.value = reslist.rows;
|
||||
|
||||
await houseStore.getCurrentVilageTreeHouse();
|
||||
getHouseRental(houseRentalid.value).then((res) => {
|
||||
HouseInfo.value = res.data;
|
||||
HouseInfo.value.facilitieslist = res.data.facilities.split(',').filter((item) => item);
|
||||
HouseInfo.value.houseImageUrls = HouseInfo.value.houseImageUrl.split(',').filter((item) => item);
|
||||
const arr = getHousePathById(treedata.value, HouseInfo.value.houseId, 'label');
|
||||
HouseInfo.value.house = arr.join(' - ');
|
||||
});
|
||||
}
|
||||
//.╬
|
||||
init();
|
||||
|
||||
function handlefacilities(id: string) {
|
||||
|
||||
@@ -125,6 +125,7 @@ const route = useRoute();
|
||||
const formRef = ref();
|
||||
const form = ref<ResidentForm>({
|
||||
houseId: null,
|
||||
unitNoId: null,
|
||||
name: '',
|
||||
gender: '0',
|
||||
idCard: '',
|
||||
@@ -178,8 +179,7 @@ function gettreedata() {
|
||||
form.value = {
|
||||
...res.data
|
||||
};
|
||||
const arr = getHousePathById(treedata.value, form.value.houseId);
|
||||
userHousepath.value = arr;
|
||||
userHousepath.value = [form.value.buildingId, form.value.unitNoId, form.value.houseId];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="证件号" min-width="120" align="center" prop="idCard" />
|
||||
<el-table-column label="手机号码" min-width="120" align="center" prop="phone" />
|
||||
<el-table-column label="房间" min-width="120" align="center" prop="houseAddress" />
|
||||
<el-table-column label="房间" min-width="120" align="center" prop="houseName" />
|
||||
<el-table-column label="操作" align="center" min-width="120" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button v-if="scope.row.status !== '1'" link type="primary" @click="handleMain(scope.row)" v-has-permi="['resident:resident:edit']"
|
||||
@@ -98,7 +98,6 @@ import DictTag from '@/components/DictTag/index.vue';
|
||||
|
||||
import { listResident, delResident } from '@/api/system/resident';
|
||||
import { ResidentVO, ResidentQuery, ResidentForm } from '@/api/system/resident/type';
|
||||
import { getHousePathById } from '@/utils/house';
|
||||
import { useHouseStore } from '@/store/modules/house';
|
||||
import { checkPermi } from '@/utils/permission';
|
||||
|
||||
@@ -229,10 +228,6 @@ const getList = async () => {
|
||||
listResident(queryParams.value)
|
||||
.then((res) => {
|
||||
residentList.value = res.rows;
|
||||
residentList.value.forEach((item) => {
|
||||
const arr = getHousePathById(treated.value, item.houseId, 'label');
|
||||
item.houseAddress = arr.join(' ');
|
||||
});
|
||||
total.value = res.total;
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -324,6 +319,9 @@ onMounted(() => {
|
||||
width: 250px;
|
||||
min-width: 250px;
|
||||
margin-right: 10px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.treeTitlebox {
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
@@ -333,7 +331,11 @@ onMounted(() => {
|
||||
background-color: rgba(233, 242, 255, 0.5);
|
||||
color: rgba(16, 16, 16, 1);
|
||||
}
|
||||
|
||||
.treeboxbody {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
margin-top: 10px;
|
||||
|
||||
&:deep(.el-tree-node__content) {
|
||||
|
||||
@@ -70,7 +70,7 @@ import { allocatedUserList, authUserCancel, authUserCancelAll } from '@/api/syst
|
||||
import { UserQuery } from '@/api/system/user/types';
|
||||
import { UserVO } from '@/api/system/user/types';
|
||||
import SelectUser from './selectUser.vue';
|
||||
import { RouteLocationNormalized } from 'vue-router';
|
||||
import { RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -115,7 +115,7 @@ const handleClose = () => {
|
||||
query: undefined,
|
||||
redirectedFrom: undefined
|
||||
};
|
||||
proxy?.$tab.closeOpenPage(obj);
|
||||
proxy?.$tab.closeOpenPage(obj as RouteLocationRaw);
|
||||
};
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<script name="RingProgress" setup lang="ts">
|
||||
import { EChartsOption } from 'echarts';
|
||||
import Echarts from '@/components/Echarts/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
value: string;
|
||||
color: string;
|
||||
}>();
|
||||
|
||||
console.log(props.name, props.value);
|
||||
|
||||
const options = ref<EChartsOption>({
|
||||
tooltip: {
|
||||
show: false,
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}%'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['50%', '80%'], // 环形大小
|
||||
center: ['50%', '50%'], // 每个图表各自居中
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
|
||||
data: [
|
||||
{
|
||||
value: 100 - Number(props.value),
|
||||
itemStyle: {
|
||||
color: '#ebeef5' // 背景色
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#ebeef5'
|
||||
},
|
||||
scale: false
|
||||
}
|
||||
},
|
||||
{
|
||||
value: Number(props.value),
|
||||
name: props.name,
|
||||
itemStyle: { color: props.color, borderRadius: 8, borderColor: props.color, borderWidth: 0 },
|
||||
label: {
|
||||
show: true,
|
||||
position: 'center', // 显示在圆环中心
|
||||
formatter: '{c}%', // 显示百分比数值
|
||||
// fontSize: '50%',
|
||||
// fontWeight: 'bold',
|
||||
color: '#4ce19c',
|
||||
rich: {
|
||||
num: {
|
||||
fontSize: '28%', // 富文本内支持百分比,相对圆环直径
|
||||
fontWeight: 'bold',
|
||||
color: '#4ce19c'
|
||||
}
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
scale: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="chart-item flex flex-col items-center" style="flex: 1; height: 100%">
|
||||
<Echarts :options="options as EChartsOption" style="width: 100%; height: 165px"></Echarts>
|
||||
<div class="chart-item-name">{{ props.name }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.chart-item {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.chart-item-name {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
@@ -13,47 +13,42 @@
|
||||
<el-button type="primary" @click="querySearch">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px; position: relative; z-index: 2">
|
||||
<div style="position: relative; z-index: 2">
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="14" :xl="14">
|
||||
<el-col :lg="14" :xl="14" style="margin-bottom: 15px">
|
||||
<div class="cardbox">
|
||||
<div>收入来源</div>
|
||||
<div class="card_infoall">
|
||||
<div>应缴费用:{{ formatMoneyWithUnit(form.totalDue) }}</div>
|
||||
<div>已收:{{ formatMoneyWithUnit(form.totalPaid) }}</div>
|
||||
<div>待收:{{ formatMoneyWithUnit(form.totalUnpaid) }}</div>
|
||||
<div class="card_InfoAll">
|
||||
<div>应缴费用:{{ formatMoneyWithUnit(sourceFrom.totalPayable) }}</div>
|
||||
<div>已收:{{ formatMoneyWithUnit(sourceFrom.totalReceived) }}</div>
|
||||
<div>待收:{{ formatMoneyWithUnit(sourceFrom.totalPending) }}</div>
|
||||
</div>
|
||||
<div class="cardbody" style="height: 165px">
|
||||
<div
|
||||
v-for="(opt, index) in incomeChartOptions"
|
||||
:key="index"
|
||||
class="chart-item flex flex-col items-center"
|
||||
style="width: 165px; height: 100%"
|
||||
>
|
||||
<Echarts :options="opt.options ?? []" style="width: 100%; height: 165px"></Echarts>
|
||||
<!-- 如果需要额外显示名称,可以在这里加 div,但图表中心已显示 -->
|
||||
<div class="chart-item-name">{{ opt.name }}</div>
|
||||
</div>
|
||||
<div class="cardBody" style="display: flex; flex-wrap: wrap" v-if="sourceFromIsComputed">
|
||||
<RingProgress name="停车费" color="#1a75ff" :value="sourceFrom.parkingFeePercent"></RingProgress>
|
||||
<RingProgress name="物业费" color="#ffbf05" :value="sourceFrom.propertyFeePercent"></RingProgress>
|
||||
<RingProgress name="水电费" color="#6c76f4" :value="sourceFrom.utilityFeePercent"></RingProgress>
|
||||
<RingProgress name="商铺租金" color="#fa746b" :value="sourceFrom.shopRentPercent"></RingProgress>
|
||||
<RingProgress name="维修费" color="#3fdf95" :value="sourceFrom.repairFeePercent"></RingProgress>
|
||||
<RingProgress name="垃圾处理费" color="#1e1b39" :value="sourceFrom.garbageFeePercent"></RingProgress>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="10" :xl="10">
|
||||
<el-col :lg="10" :xl="10" style="margin-bottom: 15px">
|
||||
<div class="cardbox">
|
||||
<div>欠费分布</div>
|
||||
<div class="cardbody w-full" style="height: 150px">
|
||||
<div>缴费方式</div>
|
||||
<div class="cardBody w-full" style="height: 100%">
|
||||
<Echarts :options="options2"></Echarts>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px; position: relative; z-index: 2">
|
||||
<div style="position: relative; z-index: 2">
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="12" :xl="14">
|
||||
<el-col :lg="12" :xl="14" style="margin-bottom: 15px">
|
||||
<div class="cardbox w-full">
|
||||
<div>欠费分布</div>
|
||||
<div class="cardbody w-full" style="height: 330px">
|
||||
<div class="cardBody w-full" style="height: 330px">
|
||||
<Echarts :options="options3"></Echarts>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,7 +67,7 @@
|
||||
<span class="ml-2">{{ item.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="circlebox">
|
||||
<ul class="circleBox">
|
||||
<li :data-level="index" :style="{ backgroundColor: item.color }" v-for="(item, index) in sortPayList" :key="index">
|
||||
{{ item.number }}%
|
||||
</li>
|
||||
@@ -90,7 +85,7 @@
|
||||
</div> -->
|
||||
<div class="cardbox w-full">
|
||||
<div>缴费走势分析</div>
|
||||
<div class="cardbody w-full" style="height: 400px">
|
||||
<div class="cardBody w-full" style="height: 400px">
|
||||
<Echarts :options="flowLineOption"></Echarts>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,59 +105,48 @@ import Echarts from '@/components/Echarts/index.vue';
|
||||
import { formatMoneyWithUnit } from '@/utils/money';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { getLast7Days, getPastDayDate } from '@/utils/time';
|
||||
import { income_statsApi } from '@/api/system/Payment';
|
||||
|
||||
const open = ref(false);
|
||||
const dataTime = ref('2025-01-01');
|
||||
const form = ref({
|
||||
'totalDue': '',
|
||||
'totalPaid': '',
|
||||
'totalUnpaid': '',
|
||||
'paidPercentage': '',
|
||||
'payWx': null,
|
||||
'payZfb': null,
|
||||
'payOffline': null,
|
||||
'totalPay': null,
|
||||
'feeTypeStats': [],
|
||||
'dailyStats': []
|
||||
});
|
||||
import {
|
||||
arrears_distributionApi,
|
||||
Arrears_distrubution,
|
||||
income_scourcApi,
|
||||
payment_methodApi,
|
||||
payment_sourceApi,
|
||||
payment_trendApi,
|
||||
payment_trendType,
|
||||
PayMentMethod
|
||||
} from '@/api/system/Payment';
|
||||
import RingProgress from './compontents/RingProgress.vue';
|
||||
|
||||
const defaultDay = 30; // 默认查询近30天数据
|
||||
const open = ref(false);
|
||||
const last10Day = getPastDayDate(defaultDay);
|
||||
const dataTime = ref(last10Day);
|
||||
|
||||
const sourceFromIsComputed = ref(true);
|
||||
/** 收入来源 */
|
||||
/** 收入来源图表配置 */
|
||||
const incomeChartOptions = ref<
|
||||
{
|
||||
name: string;
|
||||
options?: EChartsOption;
|
||||
}[]
|
||||
>(
|
||||
[
|
||||
{
|
||||
'name': '停车费'
|
||||
},
|
||||
{
|
||||
'name': '物业费'
|
||||
},
|
||||
{
|
||||
'name': '水电费'
|
||||
},
|
||||
{
|
||||
'name': '商铺租金'
|
||||
},
|
||||
{
|
||||
'name': '维修费'
|
||||
}
|
||||
].map((i) => {
|
||||
return {
|
||||
name: i.name,
|
||||
options: {
|
||||
tooltip: {},
|
||||
series: []
|
||||
}
|
||||
};
|
||||
})
|
||||
);
|
||||
const sourceFrom = ref({
|
||||
'totalPayable': '0',
|
||||
'totalReceived': '0',
|
||||
'totalPending': '0',
|
||||
|
||||
'parkingFee': '0',
|
||||
'parkingFeePercent': '0',
|
||||
|
||||
'propertyFee': '0',
|
||||
'propertyFeePercent': '0',
|
||||
|
||||
'utilityFee': '0',
|
||||
'utilityFeePercent': '0',
|
||||
|
||||
'shopRent': '0',
|
||||
'shopRentPercent': '0',
|
||||
|
||||
'repairFee': '0',
|
||||
'repairFeePercent': '0',
|
||||
|
||||
'garbageFee': '0',
|
||||
'garbageFeePercent': '0'
|
||||
});
|
||||
|
||||
/** 缴费方式 */
|
||||
const options2 = shallowRef<EChartsOption>({
|
||||
@@ -199,23 +183,54 @@ const options2 = shallowRef<EChartsOption>({
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 50, name: '支付宝', itemStyle: { color: '#0a74fe' } },
|
||||
{ value: 30, name: '微信', itemStyle: { color: '#f74043' } },
|
||||
{ value: 20, name: '线下', itemStyle: { color: '#fcd152' } }
|
||||
{ value: 0, name: '支付宝', itemStyle: { color: '#0a74fe' } },
|
||||
{ value: 0, name: '微信', itemStyle: { color: '#f74043' } },
|
||||
{ value: 0, name: '线下', itemStyle: { color: '#fcd152' } }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
function updatePayMethod(dataFrom: PayMentMethod): EChartsOption {
|
||||
return {
|
||||
legend: {
|
||||
top: 'center',
|
||||
left: '0',
|
||||
orient: 'vertical',
|
||||
itemGap: 50,
|
||||
itemWidth: 12, // 图例色块宽度
|
||||
itemHeight: 12 // 图例色块高度
|
||||
},
|
||||
clockwise: true,
|
||||
series: [
|
||||
{
|
||||
name: '缴费方式',
|
||||
type: 'pie',
|
||||
startAngle: 280,
|
||||
radius: ['50%', '100%'],
|
||||
avoidLabelOverlap: false,
|
||||
left: '50%',
|
||||
emphasis: {
|
||||
scale: true,
|
||||
scaleSize: 6
|
||||
},
|
||||
data: [
|
||||
{ value: Number(dataFrom.alipayAmount) ?? 0, name: '支付宝', itemStyle: { color: '#0a74fe' } },
|
||||
{ value: Number(dataFrom.wechatAmount) ?? 0, name: '微信', itemStyle: { color: '#f74043' } },
|
||||
{ value: Number(dataFrom.offlineAmount) ?? 0, name: '线下', itemStyle: { color: '#fcd152' } }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// 【新增】定义12个月份
|
||||
const monthX = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
|
||||
/** 欠费分布 散点图 */
|
||||
/** 欠费分布 散点图 (带特效版) */
|
||||
const options3 = shallowRef<EChartsOption>({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params: any) => {
|
||||
return `${monthX[params.value[0]]}<br/>欠费金额: <b>${params.value[1]}</b> 万元`;
|
||||
return `${params.name}<br/>欠费金额: <b>${formatMoneyWithUnit(params.value[1])}</b>`;
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#eee',
|
||||
@@ -304,7 +319,92 @@ const options3 = shallowRef<EChartsOption>({
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
function UpdateOptions3(data: Arrears_distrubution[]): EChartsOption {
|
||||
const xData = data.map((item, index) => {
|
||||
return [index, item.amount];
|
||||
});
|
||||
const xMonth = data.map((item) => `${item.month}月`);
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params: any) => {
|
||||
return `${params.name}<br/>欠费金额: <b>${formatMoneyWithUnit(params.value[1])}</b>`;
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#eee',
|
||||
textStyle: { color: '#333' }
|
||||
},
|
||||
grid: {
|
||||
left: '1%',
|
||||
right: '2%',
|
||||
top: '12%',
|
||||
bottom: '12%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xMonth,
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
length: 5, // 刻度线长度
|
||||
lineStyle: { color: '#ccc' }
|
||||
},
|
||||
axisLine: { lineStyle: { color: '#eee' }, onZero: false },
|
||||
axisLabel: {
|
||||
color: '#666',
|
||||
interval: 0, // 强制显示所有月份
|
||||
margin: 12 // 标签与轴线的距离
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed',
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '金额(万元)',
|
||||
nameTextStyle: { color: '#999', padding: [0, 0, 0, 10] },
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed' // 虚线网格更轻盈
|
||||
}
|
||||
},
|
||||
axisLabel: { color: '#999', margin: 20 }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '欠费金额',
|
||||
type: 'scatter',
|
||||
symbolSize: (data: any) => {
|
||||
const value = data[1];
|
||||
return Math.min(Math.max(value * 1.5, 10), 40);
|
||||
},
|
||||
itemStyle: {
|
||||
color: (params: any) => {
|
||||
const index = params.dataIndex;
|
||||
if (index % 2 === 0) {
|
||||
return '#739ff0';
|
||||
} else {
|
||||
return '#86c9d0';
|
||||
}
|
||||
}
|
||||
},
|
||||
// 【特效 2】高亮状态(鼠标悬停)
|
||||
emphasis: {
|
||||
scale: 1.2 // 悬停时额外放大
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
// ! 进十日缴费数量情况 -------------------------------------------------------------------------------------
|
||||
// 近10天日期
|
||||
const dayX = getLast7Days(10);
|
||||
@@ -393,50 +493,65 @@ const flowLineOption = shallowRef<EChartsOption>({
|
||||
}
|
||||
]
|
||||
});
|
||||
// ! 进十日缴费数量情况 -------------------------------------------------------------------------------------
|
||||
|
||||
const paytypelist = ref([
|
||||
{ label: '微信', value: 'payWx', number: 0, color: '#4a3afe' },
|
||||
{ label: '支付宝', value: 'payZfb', number: 0, color: '#1e1b39' },
|
||||
{ label: '线下', value: 'payOffline', number: 0, color: '#c893fd' }
|
||||
const payTypeList = ref([
|
||||
{ label: '小程序', number: 0, color: '#4a3afe' },
|
||||
{ label: '线下缴费', number: 0, color: '#1e1b39' },
|
||||
{ label: '手机APP', number: 0, color: '#c893fd' }
|
||||
]);
|
||||
// 排序后
|
||||
const sortPayList = ref([]);
|
||||
const sortPayList = ref(payTypeList.value);
|
||||
|
||||
const payMethodFrom = ref({
|
||||
'wechatAmount': '0',
|
||||
'wechatPercent': '0',
|
||||
'alipayAmount': '0',
|
||||
'alipayPercent': '0',
|
||||
'offlineAmount': '0',
|
||||
'offlinePercent': '0'
|
||||
});
|
||||
|
||||
function getList() {
|
||||
const startData = getPastDayDate(defaultDay);
|
||||
income_statsApi(dataTime.value === '' ? startData : dataTime.value).then((res) => {
|
||||
form.value = res.data;
|
||||
const { payOffline, payWx, payZfb, feeTypeStats, dailyStats } = res.data;
|
||||
const total = (payOffline ?? 0) + (payWx ?? 0) + (payZfb ?? 0);
|
||||
if (total > 0) {
|
||||
form.value.payOffline = Number.isNaN((payOffline / total) * 100) ? 0 : ((payOffline / total) * 100).toFixed(2);
|
||||
form.value.payWx = Number.isNaN((payWx / total) * 100) ? 0 : ((payWx / total) * 100).toFixed(2);
|
||||
form.value.payZfb = Number.isNaN((payZfb / total) * 100) ? 0 : ((payZfb / total) * 100).toFixed(2);
|
||||
const payTypeList = JSON.parse(JSON.stringify(paytypelist.value));
|
||||
payTypeList[0].number = form.value.payWx;
|
||||
payTypeList[1].number = form.value.payZfb;
|
||||
payTypeList[2].number = form.value.payOffline;
|
||||
sortPayList.value = payTypeList.toSorted((a, b) => Number(b.number) - Number(a.number));
|
||||
}
|
||||
|
||||
// 收入来源
|
||||
// 2. 处理收入来源 (新逻辑)
|
||||
if (feeTypeStats && Array.isArray(feeTypeStats)) {
|
||||
updateIncomeChart(feeTypeStats);
|
||||
}
|
||||
|
||||
// 3. 【新增】处理缴费走势 (近十日缴费情况)
|
||||
if (dailyStats && Array.isArray(dailyStats)) {
|
||||
updateFlowLineChart(dailyStats);
|
||||
}
|
||||
sourceFromIsComputed.value = false;
|
||||
// 收入来源
|
||||
income_scourcApi(dataTime.value)
|
||||
.then((res) => {
|
||||
sourceFrom.value = res.data;
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
sourceFromIsComputed.value = true;
|
||||
});
|
||||
});
|
||||
// 缴费方式
|
||||
payment_methodApi(dataTime.value).then((res) => {
|
||||
payMethodFrom.value = res.data;
|
||||
options2.value = updatePayMethod(res.data);
|
||||
});
|
||||
// 欠费分布
|
||||
arrears_distributionApi(dataTime.value).then((res) => {
|
||||
options3.value = UpdateOptions3(res.data.items);
|
||||
});
|
||||
//缴费来源
|
||||
payment_sourceApi(dataTime.value).then((res) => {
|
||||
const payTypeListCopy = JSON.parse(JSON.stringify(payTypeList.value));
|
||||
payTypeListCopy[0].number = res.data.miniProgramPercent;
|
||||
payTypeListCopy[1].number = res.data.offlinePercent;
|
||||
payTypeListCopy[2].number = res.data.mobileAppPercent;
|
||||
sortPayList.value = payTypeListCopy.toSorted(
|
||||
(a: { number: string | number }, b: { number: string | number }) => Number(b.number) - Number(a.number)
|
||||
);
|
||||
});
|
||||
// 缴费走势
|
||||
payment_trendApi(dataTime.value, getPastDayDate(0)).then((res) => {
|
||||
updateFlowLineChart(res.data.items);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 更新缴费走势图表
|
||||
* @param stats 后端返回的 dailyStats 数组
|
||||
* @param stats 后端返回的 dailyStats 数组&
|
||||
*/
|
||||
function updateFlowLineChart(stats: any[]) {
|
||||
function updateFlowLineChart(stats: payment_trendType[]) {
|
||||
if (!stats.length) {
|
||||
flowLineOption.value.series = [];
|
||||
flowLineOption.value.xAxis.data = [];
|
||||
@@ -444,13 +559,13 @@ function updateFlowLineChart(stats: any[]) {
|
||||
}
|
||||
|
||||
// 提取数据
|
||||
const dates = stats.map((item) => item.statDate);
|
||||
const paidData = stats.map((item) => Number(item.totalPaid) || 0);
|
||||
const unpaidData = stats.map((item) => Number(item.totalUnpaid) || 0);
|
||||
const dates = stats.map((item) => item.date);
|
||||
const paidData = stats.map((item) => Number(item.paidAmount) || 0);
|
||||
const unpaidData = stats.map((item) => Number(item.unpaidAmount) || 0);
|
||||
|
||||
// 更新图表配置
|
||||
// 使用 shallowRef 时,建议替换整个对象或确保深层属性被正确追踪
|
||||
// 这里我们直接修改 series 和 xAxis 的数据
|
||||
// 这里我们直接修改 series 和 xAxis 的数据 ╬╬
|
||||
flowLineOption.value = {
|
||||
...flowLineOption.value,
|
||||
xAxis: {
|
||||
@@ -507,96 +622,12 @@ function updateFlowLineChart(stats: any[]) {
|
||||
]
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 更新收入来源图表数组
|
||||
* @param stats 后端返回的 feeTypeStats 数组
|
||||
*/
|
||||
function updateIncomeChart(stats: any[]) {
|
||||
incomeChartOptions.value = [];
|
||||
let statslist = stats;
|
||||
if (statslist.length === 0) {
|
||||
statslist = [
|
||||
{
|
||||
'feeName': '停车费'
|
||||
},
|
||||
{
|
||||
'feeName': '物业费'
|
||||
},
|
||||
{
|
||||
'feeName': '水电费'
|
||||
},
|
||||
{
|
||||
'feeName': '商铺租金'
|
||||
},
|
||||
{
|
||||
'feeName': '维修费'
|
||||
}
|
||||
];
|
||||
}
|
||||
incomeChartOptions.value = statslist.map((item) => {
|
||||
const percentage = Number(item.paidPercentage) || 0;
|
||||
const color = '#409eff';
|
||||
return {
|
||||
name: item.feeName, // 图表名称
|
||||
options: {
|
||||
tooltip: {
|
||||
show: false,
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}%'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'], // 环形大小
|
||||
center: ['50%', '50%'], // 每个图表各自居中
|
||||
select: {
|
||||
scale: false
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: 100 - percentage,
|
||||
itemStyle: {
|
||||
color: '#ebeef5' // 背景色
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#ebeef5'
|
||||
},
|
||||
scale: false
|
||||
}
|
||||
},
|
||||
{
|
||||
value: percentage,
|
||||
name: item.feeName,
|
||||
itemStyle: { color: color },
|
||||
label: {
|
||||
show: true,
|
||||
position: 'center', // 显示在圆环中心
|
||||
formatter: '{c}%', // 显示百分比数值
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
color: '#4ce19c'
|
||||
},
|
||||
emphasis: {
|
||||
scale: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
});
|
||||
console.log(incomeChartOptions.value);
|
||||
}
|
||||
|
||||
function querySearch() {
|
||||
getList();
|
||||
}
|
||||
function reset() {
|
||||
dataTime.value = '';
|
||||
dataTime.value = getPastDayDate(defaultDay);
|
||||
querySearch();
|
||||
}
|
||||
getList();
|
||||
@@ -632,6 +663,7 @@ onUnmounted(() => {
|
||||
font-size: 20px;
|
||||
padding: 20px;
|
||||
min-height: 275px;
|
||||
height: 100%;
|
||||
@media (max-width: 1600px) {
|
||||
font-size: 18px;
|
||||
}
|
||||
@@ -641,7 +673,7 @@ onUnmounted(() => {
|
||||
@media (max-width: 1200px) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.card_infoall {
|
||||
.card_InfoAll {
|
||||
padding-left: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -653,7 +685,7 @@ onUnmounted(() => {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.cardbody {
|
||||
.cardBody {
|
||||
padding: 0 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -663,10 +695,10 @@ onUnmounted(() => {
|
||||
}
|
||||
.card_leftbox {
|
||||
text-align: center;
|
||||
.labelbox {
|
||||
.labelBox {
|
||||
font-size: 16px;
|
||||
}
|
||||
.valuebox {
|
||||
.valueBox {
|
||||
font-weight: 600;
|
||||
font-size: 36px;
|
||||
color: #222222;
|
||||
@@ -683,7 +715,6 @@ onUnmounted(() => {
|
||||
li {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: left;
|
||||
@@ -700,7 +731,7 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
.circlebox {
|
||||
.circleBox {
|
||||
margin-left: 60px;
|
||||
position: relative;
|
||||
width: 300px;
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<el-col :lg="8" :xl="8">
|
||||
<div class="cardbox">
|
||||
<div>入住情况</div>
|
||||
<div class="cardbody" style="height: 200px">
|
||||
<div class="card_leftbox">
|
||||
<div class="valuebox">{{ movein.totalResident }}</div>
|
||||
<div class="labelbox">入住人数</div>
|
||||
<div class="cardBody" style="height: 200px">
|
||||
<div class="card_LeftBox">
|
||||
<div class="valueBox">{{ MoveIn.totalResident }}</div>
|
||||
<div class="labelBox">入住人数</div>
|
||||
</div>
|
||||
<div class="echartsbox h-full">
|
||||
<Echarts :options="options"></Echarts>
|
||||
<div class="echartsBox h-full">
|
||||
<Echarts :options="options as EChartsOption"></Echarts>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,16 +31,16 @@
|
||||
<el-col :lg="10" :xl="8">
|
||||
<div class="cardbox">
|
||||
<div>住户类型情况</div>
|
||||
<div class="cardbody w-full" style="height: 150px">
|
||||
<Echarts :options="options1"></Echarts>
|
||||
<div class="cardBody w-full" style="height: 180px">
|
||||
<Echarts :options="options1 as EChartsOption"></Echarts>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :lg="6" :xl="8">
|
||||
<div class="cardbox">
|
||||
<div>男女比例</div>
|
||||
<div class="cardbody w-full" style="height: 150px">
|
||||
<Echarts :options="options2"></Echarts>
|
||||
<div class="cardBody w-full" style="height: 180px">
|
||||
<Echarts :options="options2 as EChartsOption"></Echarts>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -56,7 +56,7 @@
|
||||
<el-row style="margin-top: 20px; position: relative; z-index: 2">
|
||||
<el-col :span="24">
|
||||
<div class="cardbox w-full" style="height: 450px">
|
||||
<Echarts :options="flowLineOption"></Echarts>
|
||||
<Echarts :options="flowLineOption as EChartsOption"></Echarts>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -68,12 +68,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// TODO 人员分析
|
||||
import { ref } from 'vue';
|
||||
import Echarts from '@/components/Echarts/index.vue';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { getLastNDays } from '@/utils/time';
|
||||
import { get_movein_moveout_list, get_movein_user_sex, get_movein_user_type, user_movein_list } from '@/api/system/Personnel';
|
||||
import { get_movein_moveout_list, get_movein_user_sex, get_movein_user_type, get_person_age, user_movein_list } from '@/api/system/Personnel';
|
||||
|
||||
const date = ref('');
|
||||
const open = ref(true);
|
||||
@@ -86,25 +85,29 @@ onUnmounted(() => {
|
||||
});
|
||||
|
||||
function reset() {
|
||||
date.value = '';
|
||||
date.value = getLastNDays(10)[0];
|
||||
querySelect();
|
||||
}
|
||||
|
||||
/** 入住情况 */
|
||||
const options = ref<EChartsOption>({
|
||||
tooltip: { show: false },
|
||||
tooltip: { show: true, trigger: 'item' },
|
||||
series: [
|
||||
// 1. 底层细圆环(灰色底色)
|
||||
{
|
||||
name: '总房屋',
|
||||
type: 'pie',
|
||||
radius: ['68%', '90%'], // 细一点
|
||||
clockwise: true,
|
||||
data: [100],
|
||||
itemStyle: { color: '#ebeef5' },
|
||||
emphasis: { disabled: true },
|
||||
tooltip: { show: false }, // 关键:不显示提示
|
||||
label: { show: false }
|
||||
},
|
||||
// 2. 上层粗圆环(进度条)
|
||||
{
|
||||
name: '入住房屋',
|
||||
type: 'pie',
|
||||
radius: ['65%', '98%'], // 粗一点
|
||||
clockwise: true,
|
||||
@@ -132,33 +135,39 @@ const options = ref<EChartsOption>({
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [{ value: 85 }, { value: 15, itemStyle: { color: 'transparent' } }]
|
||||
data: [
|
||||
{ value: 85 },
|
||||
{
|
||||
value: 15,
|
||||
itemStyle: { color: 'transparent' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/** 入住类型 */
|
||||
/** 住户类型 */
|
||||
const options1 = ref<EChartsOption>({
|
||||
legend: {
|
||||
top: 'center',
|
||||
left: '0',
|
||||
orient: 'vertical',
|
||||
itemGap: 50
|
||||
itemGap: 80
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
name: '住户类型',
|
||||
type: 'pie',
|
||||
radius: ['50%', '100%'],
|
||||
radius: ['45%', '95%'],
|
||||
avoidLabelOverlap: false,
|
||||
left: '50%',
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false,
|
||||
@@ -185,12 +194,15 @@ const options2 = ref<EChartsOption>({
|
||||
top: 'center',
|
||||
left: '0',
|
||||
orient: 'vertical',
|
||||
itemGap: 50
|
||||
itemGap: 80
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
clockwise: true,
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
name: '男女比例',
|
||||
type: 'pie',
|
||||
startAngle: 280,
|
||||
radius: ['50%', '100%'],
|
||||
@@ -200,9 +212,6 @@ const options2 = ref<EChartsOption>({
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false,
|
||||
@@ -221,15 +230,23 @@ const options2 = ref<EChartsOption>({
|
||||
]
|
||||
});
|
||||
|
||||
// ! 年龄分布 -------------------------------------------------------------------------------------
|
||||
// ! 年龄分布 -----------------------------------------
|
||||
const agesX = ['6岁以下', '6-11岁', '11-14岁', '15-24岁', '25-30岁', '31-40岁', '41-50岁', '51-61岁', '61-65岁', '65-70岁', '70岁以上'];
|
||||
const agesData = [520, 680, 460, 750, 1350, 1860, 1420, 960, 630, 380, 210];
|
||||
// 🔥 关键:灰色背景数据 = 最大值 - 真实数据(互补100%)
|
||||
const maxValue = 2000; // 设定总高度(可自行调整)
|
||||
const middleHeight = 20;
|
||||
const barwidth = '10%';
|
||||
const bgData = agesData.map((item) => maxValue - item - middleHeight);
|
||||
const bgData2 = agesData.map((item) => middleHeight);
|
||||
const agesData = ref(new Array(11).fill(0));
|
||||
const maxValue = ref(10); // 设定总高度
|
||||
const middleHeight = 0.05; // 中间白条高度
|
||||
const barWidth = '10%'; // 柱状图粗度
|
||||
const bgData = computed(() => {
|
||||
return agesData.value.map((item) => {
|
||||
const num = maxValue.value - item - middleHeight;
|
||||
if (num > 0) {
|
||||
return num;
|
||||
} else {
|
||||
return maxValue.value;
|
||||
}
|
||||
});
|
||||
});
|
||||
const bgData2 = agesX.map(() => middleHeight);
|
||||
/** 年龄分布 */
|
||||
const options3 = shallowRef<EChartsOption>({
|
||||
title: {
|
||||
@@ -250,19 +267,16 @@ const options3 = shallowRef<EChartsOption>({
|
||||
confine: true,
|
||||
axisPointer: { type: 'shadow' }
|
||||
},
|
||||
// 鼠标悬浮高亮当前,其他隐藏
|
||||
|
||||
xAxis: { type: 'category', data: agesX, axisLabel: { interval: 0 } },
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: { show: true, lineStyle: { color: '#eee' } }
|
||||
},
|
||||
series: [
|
||||
// 蓝色真实数据
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: barwidth,
|
||||
data: agesData,
|
||||
barWidth: barWidth,
|
||||
data: agesData.value,
|
||||
itemStyle: {
|
||||
color: '#409eff',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
@@ -271,7 +285,7 @@ const options3 = shallowRef<EChartsOption>({
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: barwidth,
|
||||
barWidth: barWidth,
|
||||
data: bgData2,
|
||||
tooltip: { show: false }, // 关键:不显示提示
|
||||
itemStyle: {
|
||||
@@ -281,8 +295,8 @@ const options3 = shallowRef<EChartsOption>({
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: barwidth,
|
||||
data: bgData,
|
||||
barWidth: barWidth,
|
||||
data: bgData.value,
|
||||
tooltip: { show: false }, // 关键:不显示提示
|
||||
itemStyle: {
|
||||
color: 'rgba(180, 180, 180, 0.2)'
|
||||
@@ -291,6 +305,65 @@ const options3 = shallowRef<EChartsOption>({
|
||||
}
|
||||
]
|
||||
});
|
||||
function updateOptions3(data: number[]): EChartsOption {
|
||||
return {
|
||||
title: {
|
||||
text: '住户年龄分布',
|
||||
left: '0',
|
||||
top: '0',
|
||||
textStyle: { fontSize: 16, fontWeight: 'bold' }
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
top: '12%',
|
||||
bottom: '8%',
|
||||
containLabel: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
confine: true,
|
||||
axisPointer: { type: 'shadow' }
|
||||
},
|
||||
xAxis: { type: 'category', data: agesX, axisLabel: { interval: 0 } },
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: { show: true, lineStyle: { color: '#eee' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: barWidth,
|
||||
data: data,
|
||||
itemStyle: {
|
||||
color: '#409eff',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
stack: 'total'
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: barWidth,
|
||||
data: bgData2,
|
||||
tooltip: { show: false }, // 关键:不显示提示
|
||||
itemStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
stack: 'total'
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: barWidth,
|
||||
data: bgData.value,
|
||||
tooltip: { show: false }, // 关键:不显示提示
|
||||
itemStyle: {
|
||||
color: 'rgba(180, 180, 180, 0.2)'
|
||||
},
|
||||
stack: 'total'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
// ! 年龄分布 -------------------------------------------------------------------------------------
|
||||
|
||||
// ! 人员流动情况 -------------------------------------------------------------------------------------
|
||||
@@ -388,7 +461,7 @@ const flowLineOption = ref<EChartsOption>({
|
||||
}
|
||||
]
|
||||
});
|
||||
function flowLineOptionFun(days: string[], indata: number[], outdata: number[]): EChartsOption {
|
||||
function flowLineOptionFun(days: string[], InData: number[], OutData: number[]): EChartsOption {
|
||||
return {
|
||||
title: {
|
||||
text: '近十日人员流动趋势',
|
||||
@@ -430,7 +503,7 @@ function flowLineOptionFun(days: string[], indata: number[], outdata: number[]):
|
||||
name: '人员流入',
|
||||
type: 'line',
|
||||
smooth: true, // 平滑折线 核心
|
||||
data: indata,
|
||||
data: InData,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
itemStyle: { color: '#f94144' },
|
||||
@@ -453,7 +526,7 @@ function flowLineOptionFun(days: string[], indata: number[], outdata: number[]):
|
||||
name: '人员流出',
|
||||
type: 'line',
|
||||
smooth: true, // 平滑折线 核心
|
||||
data: outdata,
|
||||
data: OutData,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
itemStyle: { color: '#f9c74f' },
|
||||
@@ -477,34 +550,43 @@ function flowLineOptionFun(days: string[], indata: number[], outdata: number[]):
|
||||
}
|
||||
// ! 人员流动情况 -------------------------------------------------------------------------------------
|
||||
|
||||
const movein = ref({
|
||||
const MoveIn = ref({
|
||||
totalResident: 0,
|
||||
totalHouse: 0
|
||||
});
|
||||
function init() {
|
||||
// 获取入住情况
|
||||
moveinFun();
|
||||
// 入住情况
|
||||
MoveInFun();
|
||||
// 入住类型
|
||||
moveinUserType();
|
||||
|
||||
moveinUserSex();
|
||||
moveinUserMoveinOut();
|
||||
MoveInUserType();
|
||||
// 男女类型
|
||||
MoveInUserSex();
|
||||
// 迁入迁出
|
||||
MoveInUserMoveInOut();
|
||||
// 住户年龄分布
|
||||
MoveInPersonAge();
|
||||
}
|
||||
init();
|
||||
function querySelect() {
|
||||
init();
|
||||
}
|
||||
|
||||
function moveinFun() {
|
||||
// 入住情况
|
||||
function MoveInFun() {
|
||||
user_movein_list({
|
||||
datetime: date.value
|
||||
dateTime: date.value
|
||||
}).then((res) => {
|
||||
// 获取入住情况
|
||||
movein.value.totalResident = res.data.occupancyCount;
|
||||
movein.value.totalHouse = res.data.totalHouse;
|
||||
MoveIn.value.totalResident = res.data.occupancyCount;
|
||||
MoveIn.value.totalHouse = res.data.totalHouse;
|
||||
options.value.series[1].data = [
|
||||
{ value: res.data.occupiedHouse },
|
||||
{ value: res.data.totalHouse - res.data.occupiedHouse, itemStyle: { color: 'transparent' } }
|
||||
{
|
||||
value: res.data.totalHouse - res.data.occupiedHouse,
|
||||
itemStyle: { color: 'transparent' },
|
||||
tooltip: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
];
|
||||
options.value.series[1].label.formatter = () => {
|
||||
const total = res.data.totalHouse;
|
||||
@@ -518,17 +600,22 @@ function moveinFun() {
|
||||
});
|
||||
}
|
||||
// 获取住户类型
|
||||
function moveinUserType() {
|
||||
function MoveInUserType() {
|
||||
get_movein_user_type({
|
||||
datetime: date.value
|
||||
dateTime: date.value
|
||||
}).then((res) => {
|
||||
console.log(res);
|
||||
options1.value.series[0].data = [
|
||||
{ value: res.owner, name: '业主', itemStyle: { color: '#0a74fe' } },
|
||||
{ value: res.tenant, name: '租户', itemStyle: { color: '#fe3d4e' } },
|
||||
{ value: res.relatives, name: '亲属', itemStyle: { color: '#00cf7f' } },
|
||||
{ value: res.other, name: '其他', itemStyle: { color: '#f48e3b' } }
|
||||
];
|
||||
});
|
||||
}
|
||||
// 住户性别
|
||||
function moveinUserSex() {
|
||||
function MoveInUserSex() {
|
||||
get_movein_user_sex({
|
||||
datetime: date.value
|
||||
dateTime: date.value
|
||||
}).then((res) => {
|
||||
options2.value.series[0].data = [
|
||||
{ value: res.man, name: '男', itemStyle: { color: '#0a74fe' } },
|
||||
@@ -537,11 +624,7 @@ function moveinUserSex() {
|
||||
});
|
||||
}
|
||||
// 迁入迁出
|
||||
function moveinUserMoveinOut() {
|
||||
// 判断 date.value 是否为今天
|
||||
if (date.value === '') {
|
||||
date.value = getLastNDays(7)[0];
|
||||
}
|
||||
function MoveInUserMoveInOut() {
|
||||
get_movein_moveout_list({
|
||||
date: date.value
|
||||
}).then((res) => {
|
||||
@@ -551,6 +634,17 @@ function moveinUserMoveinOut() {
|
||||
flowLineOption.value = flowLineOptionFun(dayX.value, inData.value, outData.value);
|
||||
});
|
||||
}
|
||||
// 住户年龄分布
|
||||
function MoveInPersonAge() {
|
||||
get_person_age({
|
||||
dateTime: date.value
|
||||
}).then((res) => {
|
||||
const values = Object.values(res).slice(0, -1);
|
||||
maxValue.value = res.total;
|
||||
agesData.value = values;
|
||||
options3.value = updateOptions3(values);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -571,6 +665,7 @@ function moveinUserMoveinOut() {
|
||||
}
|
||||
// 入住清空
|
||||
.cardbox {
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, #ffffff 70%);
|
||||
border-radius: 16px 16px 16px 16px;
|
||||
border: 2px solid #ffffff;
|
||||
@@ -580,25 +675,25 @@ function moveinUserMoveinOut() {
|
||||
@media (max-width: 1200px) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.cardbody {
|
||||
.cardBody {
|
||||
padding: 0 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.card_leftbox {
|
||||
.card_LeftBox {
|
||||
text-align: center;
|
||||
margin-right: 20px;
|
||||
.labelbox {
|
||||
.labelBox {
|
||||
font-size: 16px;
|
||||
}
|
||||
.valuebox {
|
||||
.valueBox {
|
||||
font-weight: 600;
|
||||
font-size: 36px;
|
||||
color: #222222;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
.echartsbox {
|
||||
.echartsBox {
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const router = useRouter();
|
||||
const storeroomList = ref<StoreroomVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
@@ -77,12 +76,6 @@ const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const storeroomFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: StoreroomForm = {
|
||||
id: undefined,
|
||||
@@ -116,7 +109,7 @@ const data = reactive<PageData<StoreroomForm, StoreroomQuery>>({
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { queryParams } = toRefs(data);
|
||||
|
||||
/** 查询储藏室管理列表 */
|
||||
const getList = async () => {
|
||||
@@ -127,18 +120,6 @@ const getList = async () => {
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
storeroomFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
@@ -166,7 +147,7 @@ const handleAdd = () => {
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: StoreroomVO) => {
|
||||
const handleUpdate = (row?: StoreroomVO) => {
|
||||
const id = row?.id || ids.value[0];
|
||||
router.push({
|
||||
path: '/house/EditStoreRoom',
|
||||
|
||||
@@ -61,7 +61,7 @@ import pagination from '@/components/Pagination/index.vue';
|
||||
import { RoleVO } from '@/api/system/role/types';
|
||||
import { getAuthRole, updateAuthRole } from '@/api/system/user';
|
||||
import { UserForm } from '@/api/system/user/types';
|
||||
import { RouteLocationNormalized } from 'vue-router';
|
||||
import { RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -112,7 +112,7 @@ const close = () => {
|
||||
redirectedFrom: undefined,
|
||||
path: '/system/user'
|
||||
};
|
||||
proxy?.$tab.closeOpenPage(obj);
|
||||
proxy?.$tab.closeOpenPage(obj as RouteLocationRaw);
|
||||
};
|
||||
/** 提交按钮 */
|
||||
const submitForm = async () => {
|
||||
|
||||
@@ -482,7 +482,7 @@ const cancel = () => {
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = async () => {
|
||||
const handleAdd = () => {
|
||||
router.push({
|
||||
path: '/system/addUser'
|
||||
});
|
||||
@@ -490,7 +490,7 @@ const handleAdd = async () => {
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: UserForm) => {
|
||||
const handleUpdate = (row?: UserForm) => {
|
||||
const userId = row?.userId || ids.value[0];
|
||||
router.push({
|
||||
path: '/system/editUser',
|
||||
@@ -548,10 +548,6 @@ onMounted(() => {
|
||||
initPassword.value = response.data;
|
||||
});
|
||||
});
|
||||
|
||||
async function handleDeptChange(value: number | string) {
|
||||
console.log(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -119,7 +119,7 @@ import { optionselect as getDictOptionselect } from '@/api/system/dict/type';
|
||||
import { DictTypeVO } from '@/api/system/dict/type/types';
|
||||
import BasicInfoForm from './basicInfoForm.vue';
|
||||
import GenInfoForm from './genInfoForm.vue';
|
||||
import { RouteLocationNormalized } from 'vue-router';
|
||||
import { RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -179,7 +179,7 @@ const close = () => {
|
||||
redirectedFrom: undefined,
|
||||
query: { t: Date.now().toString(), pageNum: route.query.pageNum }
|
||||
};
|
||||
proxy?.$tab.closeOpenPage(obj);
|
||||
proxy?.$tab.closeOpenPage(obj as RouteLocationRaw);
|
||||
};
|
||||
|
||||
(async () => {
|
||||
|
||||
Reference in New Issue
Block a user