修正ts类型

This commit is contained in:
Zy
2026-05-13 15:28:25 +08:00
parent 1b738c0f4d
commit a1750bc67b
114 changed files with 2346 additions and 613 deletions

View File

@@ -35,11 +35,12 @@
"echarts": "6.0.0",
"element-china-area-data": "^6.1.0",
"element-plus": "2.13.5",
"file-saver": "2.0.5",
"file-saver": "^2.0.5",
"highlight.js": "11.11.1",
"image-conversion": "2.1.1",
"js-cookie": "3.0.5",
"jsencrypt": "3.5.4",
"jszip": "^3.10.1",
"nprogress": "0.2.0",
"pinia": "3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
@@ -56,7 +57,7 @@
"devDependencies": {
"@iconify/json": "^2.2.448",
"@types/crypto-js": "4.2.2",
"@types/file-saver": "2.0.7",
"@types/file-saver": "^2.0.7",
"@types/js-cookie": "3.0.6",
"@types/node": "^25.4.0",
"@types/nprogress": "0.2.3",

View File

@@ -61,3 +61,18 @@ export const delBanner = (id: string | number | Array<string | number>) => {
method: 'delete'
});
};
/**
* 改变Banner图管理 状态
* @param id
*/
export const switchBanner = (id: string, status: string) => {
return request({
url: '/banner/changeStatus',
method: 'GET',
params: {
id,
status
}
});
};

View File

@@ -17,9 +17,9 @@ export interface BannerVO {
/**
* 排序
*/
order: number;
url: string;
jump_link: string;
byOrder: number;
link: string;
/**
* 小区id
*/
@@ -45,7 +45,8 @@ export interface BannerForm extends BaseEntity {
/**
* 排序
*/
order?: number;
byOrder?: number;
link: string;
/**
* 小区id
@@ -67,8 +68,8 @@ export interface BannerQuery extends PageQuery {
/**
* 排序
*/
order?: number;
byOrder: number;
link: string;
/**
* 小区id
*/

View File

@@ -61,3 +61,15 @@ export const delPlan = (id: string | number | Array<string | number>) => {
method: 'delete'
});
};
/**
* 改变巡检计划状态
* @param id
* @param status
*/
export const changePlanStatus = (id: string, status: string) => {
return request({
url: '/inspection/plan/changeStatus',
method: 'put',
data: { id, status }
});
};

View File

@@ -61,3 +61,13 @@ export const delPoint = (id: string | number | Array<string | number>) => {
method: 'delete'
});
};
/**
* 修改巡检点状态
* @param id
*/
export const changePointStatus = (id: string, status: string) => {
return request({
url: '/inspection/point/changeStatus?id=' + id + '&status=' + status,
method: 'PUT'
});
};

View File

@@ -1,15 +1,15 @@
export interface RecordVO {
actualEndTime?: null;
actualStartTime?: null;
createByName?: null;
actualEndTime?: string;
actualStartTime?: string;
createByName?: string;
createTime?: string;
details?: null;
details?: [];
id?: string;
inspectorId?: number;
inspectorName?: string;
planId?: string;
planName?: string;
remark?: null;
remark?: string;
routeId?: string;
routeName?: string;
status?: number;

View File

@@ -26,6 +26,8 @@ export interface RouteVO {
*/
estimatedTime: number;
items: { itemName: string }[];
PointsName: string;
/**
* 备注
*/

View File

@@ -22,12 +22,12 @@ export interface NoticeVO {
/**
* 紧急程度 0=普通 1=紧急 2=非常紧急
*/
urgencyLevel: string;
urgencyLevel: number;
/**
* 发布状态 0=草稿 1=已发布
*/
publishStatus: string;
publishStatus: number;
/**
* 发布人
@@ -64,12 +64,12 @@ export interface NoticeForm extends BaseEntity {
/**
* 紧急程度 0=普通 1=紧急 2=非常紧急
*/
urgencyLevel?: string;
urgencyLevel?: number;
/**
* 发布状态 0=草稿 1=已发布
*/
publishStatus?: string;
publishStatus?: number;
/**
* 发布人
@@ -101,12 +101,12 @@ export interface NoticeQuery extends PageQuery {
/**
* 紧急程度 0=普通 1=紧急 2=非常紧急
*/
urgencyLevel?: string;
urgencyLevel?: number;
/**
* 发布状态 0=草稿 1=已发布
*/
publishStatus?: string;
publishStatus?: number;
/**
* 发布人

View File

@@ -12,7 +12,7 @@ export interface ParkingCostVO {
/**
* 车位id
*/
parkingId: string | number;
parkingId: string;
/**
* 收费项目 0临时停车
@@ -79,7 +79,7 @@ export interface ParkingCostForm extends BaseEntity {
/**
* 车位id
*/
parkingId?: string | number;
parkingId?: string;
/**
* 收费项目 0临时停车
@@ -141,7 +141,7 @@ export interface ParkingCostQuery extends PageQuery {
/**
* 车位id
*/
parkingId?: string | number;
parkingId?: string;
/**
* 收费项目 0临时停车

View File

@@ -37,7 +37,9 @@ export interface QuestionnaireVO {
/**
* 部分业主串 ”,“号隔开
*/
residents: string | number;
residents: string;
startTime: string;
endTime: string;
/**
* 备注
@@ -69,7 +71,7 @@ export interface QuestionnaireForm extends BaseEntity {
/**
* 问卷题目结构JSON数组
*/
content?: string;
content?: string | QuestionComponentItem[];
/**
* 状态0-草稿 1-已发布 2-已结束
@@ -158,6 +160,6 @@ export interface QuestionComponentItem {
export interface QuestionComponentnaireForm {
id?: string | number | null;
title: string;
description: string;
description?: string;
content: QuestionComponentItem[];
}

View File

@@ -57,7 +57,7 @@ export interface RepairVO {
/**
* 维修人员id
*/
repairId: string | number;
repairId: string;
/** 名称(小区名-楼栋名-单元号-房间号) */
name?: string;
}

View File

@@ -2,7 +2,7 @@ export interface RepairProjectVO {
/**
* 维修项目表id
*/
id: string | number;
id: string;
/**
* 维修项目名
@@ -12,7 +12,7 @@ export interface RepairProjectVO {
/**
* 父级项目id
*/
parentId: string | number;
parentId: string;
/**
* 项目类型 0公共报修 1个人报修
@@ -23,7 +23,7 @@ export interface RepairProjectVO {
* 项目层级 01
*/
projectLevel: number;
children?: [];
/**
* 排序 (数越小越靠前)
*/
@@ -39,7 +39,7 @@ export interface RepairProjectForm extends BaseEntity {
/**
* 维修项目表id
*/
id?: string | number;
id?: string;
/**
* 维修项目名
@@ -49,7 +49,7 @@ export interface RepairProjectForm extends BaseEntity {
/**
* 父级项目id
*/
parentId?: string | number;
parentId?: string;
/**
* 项目类型 0公共报修 1个人报修
@@ -101,7 +101,7 @@ export interface RepairProjectQuery extends PageQuery {
/**
* 父级项目id
*/
parentId?: string | number;
parentId?: string;
/**
* 项目类型 0公共报修 1个人报修

View File

@@ -117,6 +117,8 @@ export interface BillQuery extends PageQuery {
*/
chargeItemId?: string | number;
// type: string;
/**
* 收费范围 0全体业主 1部分业主
*/

View File

@@ -96,3 +96,14 @@ export const WaterDeviceUpdateBin = (data: { deviceCodes: string[]; urlA: string
data: data
});
};
/**
* 获取设备最新的抄表记录
* @param deviceCodes
*/
export const latestRecord = (deviceCodes: string) => {
return request({
url: `/waterDevice/latestRecord/${deviceCodes}`,
method: 'GET'
});
};

View File

@@ -2,7 +2,7 @@ export interface WarehouseVO {
/**
* 仓库ID
*/
id: string | number;
id: string;
/**
* 仓库名称*
@@ -12,7 +12,7 @@ export interface WarehouseVO {
/**
* 仓库归属id*关联com_village
*/
villageId: number;
villageId: string;
/**
* 联系人*
@@ -44,7 +44,7 @@ export interface WarehouseForm extends BaseEntity {
/**
* 仓库ID
*/
id?: string | number;
id?: string;
/**
* 仓库名称*

View File

@@ -17,7 +17,7 @@ export interface CarVO {
/**
* 持有人id(住户表id)
*/
residentId: string | number;
residentId: string;
/**
* 车牌号

View File

@@ -12,7 +12,7 @@ export interface DeviceCategoryVO {
/**
* 状态 0正常 1停用
*/
status: string;
status: number;
/**
* 备注
@@ -34,7 +34,7 @@ export interface DeviceCategoryForm extends BaseEntity {
/**
* 状态 0正常 1停用
*/
status?: string;
status?: number;
/**
* 备注
@@ -51,7 +51,7 @@ export interface DeviceCategoryQuery extends PageQuery {
/**
* 状态 0正常 1停用
*/
status?: string;
status?: number;
/**
* 日期范围参数

View File

@@ -1,4 +1,5 @@
export interface DeviceVO {
categoryName: string;
/**
* 主键ID
*/

View File

@@ -46,7 +46,7 @@ export function editBuildingApi(data: addBuildingType & { id: string }) {
});
}
/** 删除楼栋 */
export function deleteBuildingApi(id: number) {
export function deleteBuildingApi(id: string) {
return request({
url: `/building/${id}`,
method: 'DELETE'
@@ -55,9 +55,9 @@ export function deleteBuildingApi(id: number) {
// 房屋----------------------------------------------------------------------------------------------
/** 根据 楼栋id 查询房屋信息 */
export function getHouselistAPI(buildingId: string): AxiosPromise<BuildingUnit> {
export function getHouselistAPI(buildingId: string, houseName: string = ''): AxiosPromise<BuildingUnit> {
return request({
url: `/house/groupByUnitTree/${buildingId}`,
url: `/house/groupByUnitTree/${buildingId}?houseName=${houseName}`,
method: 'get'
});
}

View File

@@ -2,7 +2,7 @@ export interface HouseRentalVO {
/**
* id 房屋租赁表id
*/
id?: string | number;
id: string | number;
/**
* 租户姓名
@@ -109,7 +109,7 @@ export interface HouseRentalForm extends BaseEntity {
/**
* 0 整租 1 合租
*/
rentalType?: number;
rentalType?: string;
/**
* 房屋朝向
@@ -171,7 +171,7 @@ export interface HouseRentalQuery extends PageQuery {
/**
* 房屋用途
*/
houseUse?: number;
houseUse?: string;
/**
* 租户姓名
*/
@@ -185,7 +185,7 @@ export interface HouseRentalQuery extends PageQuery {
/**
* 0 整租 1 合租
*/
rentalType?: number;
rentalType?: string;
/**
* 房屋朝向

View File

@@ -61,3 +61,26 @@ export const delParking = (id: string | number | Array<string | number>) => {
method: 'delete'
});
};
/**
* 改变车位审核装填
* @param id
*/
export const getParkingReview = (id: string) => {
return request({
url: '/parkingReview/checkStatus',
method: 'POST'
});
};
/**
* 获取车位审核信息
* @param id
*/
export const getParkingReviewInfo = (id: string) => {
return request({
url: '/parking/checkInfo',
method: 'POST',
data: {
id
}
});
};

View File

@@ -32,7 +32,7 @@ export interface ParkingVO {
/**
* 住户id
*/
residentId: string | number;
residentId: string;
/**
* 添加时间
@@ -88,7 +88,7 @@ export interface ParkingForm extends BaseEntity {
/**
* 住户id
*/
residentId?: string | number;
residentId?: string;
/**
* 添加时间
@@ -160,7 +160,7 @@ export interface ParkingQuery extends PageQuery {
/**
* 住户id
*/
residentId?: string | number;
residentId?: string;
/**
* 添加时间

View File

@@ -22,7 +22,7 @@ export interface ResidentVO {
/**
* 0 男性 1女性
*/
gender: number;
gender: string;
/**
* 身份证号
@@ -97,7 +97,7 @@ export interface ResidentForm extends BaseEntity {
/**
* 0 男性 1女性
*/
gender?: number;
gender?: string;
/**
* 身份证号

View File

@@ -29,7 +29,7 @@ export interface UserQuery extends PageQuery {
* 用户返回对象
*/
export interface UserVO extends BaseEntity {
userId: string | number;
userId: string;
tenantId: string;
deptId: number;
userName: string;

View File

@@ -1,8 +1,9 @@
export interface CategoryTreeVO {
id: number | string;
id: string;
label: string;
parentId: number | string;
weight: number;
categoryName: string;
children: CategoryTreeVO[];
}
export interface CategoryVO {

View File

@@ -12,6 +12,8 @@ export const workSpaceTotalListApi = (
totalResident: number;
totalHouse: number;
totalParking: number;
totalDevice: number;
totalMaterial: number;
totalComplaint: number;
}> => {
return request({
@@ -22,3 +24,263 @@ export const workSpaceTotalListApi = (
}
});
};
/**
* 查询近一周报修趋势
* @param query
* @returns {*}
*/
export const workSpaceRepairTrendListApi = (
villageId: string
): Promise<
{
date: string;
count: number;
}[]
> => {
return request({
url: '/workSpace/last7DaysRepairCount',
method: 'GET',
params: {
villageId: villageId
}
});
};
/**
* 查询近一周收入趋势
* @param query
* @returns {*}
*/
export const workSpaceIncomeTrendListApi = (
villageId: string,
type: number
): Promise<
{
date: string;
income: number;
}[]
> => {
return request({
url: '/workSpace/last7DaysIncomeTrend',
method: 'GET',
params: {
villageId: villageId,
type: type
}
});
};
/**
* 查询当日天气
* @param query
* @returns {*}
*/
export const workSpaceWeatherApi = (city: string): Promise<reponseType> => {
const url = 'https://uapis.cn/api/v1/misc/weather?city=' + city + '&lang=zh';
return new Promise((resolve, reject) => {
fetch(url, {
method: 'GET'
})
.then((res) => res.json())
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
};
type reponseType = {
// 省份
'province': string;
// 城市名
'city': string;
// 区县或更细一级的行政区名称。自动按 IP 定位时更常见。
'district': string;
// 行政区划代码(部分数据源可能为空)
'adcode': string;
// 天气状况描述。默认返回中文,传 `lang=en` 时返回英文。非固定枚举。
'weather': string;
// 天气图标代码。请从[天气图标代码表](#enum-list)中查看所有可能的值。
'weather_icon': string;
// 当前温度 °C
'temperature': number;
// 风向
'wind_direction': string;
// 风力等级
'wind_power': string;
// 相对湿度 %
'humidity': number;
// 数据更新时间
'report_time': string;
// 体感温度 °Cextended=true 时返回)
'feels_like': number;
// 能见度 kmextended=true 时返回)
'visibility': number;
// 气压 hPaextended=true 时返回)
'pressure': number;
// 紫外线指数extended=true 时返回)
'uv': number;
// 当前降水量 mmextended=true 时返回)
'precipitation': number;
// 云量 %extended=true 时返回)
'cloud': number;
// 空气质量指数 0-500extended=true 时返回)
'aqi': number;
// AQI 等级 1-6extended=true 时返回)
'aqi_level': number;
// AQI 等级描述(优/良/轻度污染/中度污染/重度污染/严重污染extended=true 时返回)
'aqi_category': string;
// 主要污染物(如 PM2.5、PM10、O3 等extended=true 时返回)
'aqi_primary': string;
// 空气污染物分项数据extended=true 时返回,部分数据源可能不返回)
'air_pollutants': {
// PM2.5 μg/m³
'pm25': number;
// PM10 μg/m³
'pm10': number;
// 臭氧 μg/m³
'o3': number;
// 二氧化氮 μg/m³
'no2': number;
// 二氧化硫 μg/m³
'so2': number;
// 一氧化碳 mg/m³
'co': number;
};
// 官方气象预警列表(存在有效预警时返回)
'alerts': {
// 预警标题
'title': string;
// 预警类型,如雷电、暴雨
'type': string;
// 预警级别,如蓝色、黄色、橙色、红色
'level': string;
// 预警正文
'text': string;
// 预警发布时间
'publish_time': string;
// 发布单位
'publisher': string;
// 防御指引列表
'guidance': string[];
}[];
// 当天最高温 °Cforecast=true 时返回)
'temp_max': number;
// 当天最低温 °Cforecast=true 时返回)
'temp_min': number;
// 多天天气预报最多7天forecast=true 时返回)
'forecast': {
// 日期 YYYY-MM-DD
'date': string;
// 星期几(`lang=en` 时返回英文星期)
'week': string;
// 最高温度 °C
'temp_max': number;
// 最低温度 °C
'temp_min': number;
// 白天天气(`lang=en` 时返回英文)
'weather_day': string;
// 夜间天气(`lang=en` 时返回英文)
'weather_night': string;
// 白天风向(可选,`lang=en` 时返回英文)
'wind_dir_day': string;
// 夜间风向(可选,`lang=en` 时返回英文)
'wind_dir_night': string;
// 白天风力(可选,`lang=en` 时返回英文)
'wind_scale_day': string;
// 夜间风力(可选,`lang=en` 时返回英文)
'wind_scale_night': string;
// 白天风速 km/h可选
'wind_speed_day': number;
// 湿度 %(可选)
'humidity': number;
// 降水量 mm可选
'precip': number;
// 能见度 km可选
'visibility': number;
// 紫外线指数(可选)
'uv_index': number;
// 日出时间 HH:MM可选
'sunrise': string;
// 日落时间 HH:MM可选
'sunset': string;
}[];
// 逐小时预报最多24小时hourly=true 时返回)
'hourly_forecast': {
// 预报时间ISO8601 或 YYYY-MM-DD HH:MM
'time': string;
// 温度 °C
'temperature': number;
// 天气状况
'weather': string;
// 风向(可选)
'wind_direction': string;
// 风速 km/h可选
'wind_speed': number;
// 风力等级(可选)
'wind_scale': string;
// 湿度 %(可选)
'humidity': number;
// 降水量 mm可选
'precip': number;
// 体感温度 °C可选
'feels_like': number;
// 能见度 km可选
'visibility': number;
// 降水概率 %(可选)
'pop': number;
// 紫外线指数(可选,国内城市通常不返回)
'uv_index': number;
}[];
// 分钟级降水预报minutely=true 时返回仅国内城市可用精确到2分钟
'minutely_precip': {
// 降水描述
'summary': string;
// 更新时间
'update_time': string;
// 精确到2分钟的数据点
'data': {
// 预报时间 ISO8601
'time': string;
// 该时间点的降水量 mm
'precip': number;
// 降水类型rain / snow
'type': string;
}[];
};
// 18项生活指数indices=true 时返回),每项包含 level等级名称、brief简短描述、advice详细建议
'life_indices': LifeIndices;
};
// 1. 定义单个生活指数的基础结构
type LifeIndexItem = {
level: string;
brief: string;
advice: string;
};
// 2. 定义所有可能的键名
type LifeIndexKeys =
| 'clothing'
| 'uv'
| 'car_wash'
| 'drying'
| 'air_conditioner'
| 'cold_risk'
| 'exercise'
| 'comfort'
| 'travel'
| 'fishing'
| 'allergy'
| 'sunscreen'
| 'mood'
| 'beer'
| 'umbrella'
| 'traffic'
| 'air_purifier'
| 'pollen';
// 3. 使用映射类型自动生成完整结构
type LifeIndices = {
[K in LifeIndexKeys]: LifeIndexItem;
};

View File

@@ -5,7 +5,7 @@ export interface FlowInstanceQuery extends PageQuery {
nodeName?: string;
flowCode?: string;
flowName?: string;
createByIds?: string[] | number[];
createByIds?: string[];
businessId?: string;
}

View File

@@ -3,7 +3,7 @@
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta?.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a>
<span class="no-redirect" v-else>{{ item.meta?.title }}</span>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>

View File

@@ -221,7 +221,7 @@ function resetQuery() {
queryParams.value = {
pageNum: 1,
pageSize: 10,
houseuse: null,
buildingUse: null,
buildingId: null,
unitNo: null
};

View File

@@ -81,10 +81,7 @@ const dialog = ref({
const queryParams = ref({
pageNum: 1,
pageSize: 10,
buildingUse: null,
buildingId: null,
unitNo: null
pageSize: 10
});
const multipleTableRef = ref();
@@ -198,10 +195,7 @@ function handleRowClick(row: BillHouse) {
function resetQuery() {
queryParams.value = {
pageNum: 1,
pageSize: 10,
houseuse: null,
buildingId: null,
unitNo: null
pageSize: 10
};
getlist();
}

View File

@@ -0,0 +1,288 @@
<template>
<div class="license-plate-input">
<div class="plate_line">
<div
v-for="(item, index) in chars"
:key="index"
class="plate-item"
:class="{
active: activeIndex === index,
disabled: disabled,
error: hasError
}"
>
<input
ref="inputRefs"
v-model="chars[index]"
type="text"
maxlength="1"
:disabled="disabled"
@input="onInput(index)"
@keydown="onKeydown($event, index)"
@paste="onPaste"
@focus="activeIndex = index"
@blur="onBlur"
/>
</div>
</div>
<!-- 错误提示 Element Plus 原生完全一致 -->
<div v-if="errorMessage" class="plate-error">{{ errorMessage }}</div>
</div>
</template>
<script setup lang="ts">
import { LicensePlateValidator } from '@/utils/Reg';
import { ref, watch, nextTick } from 'vue';
const PROVINCES = [
'京',
'津',
'沪',
'渝',
'冀',
'豫',
'云',
'辽',
'黑',
'湘',
'皖',
'鲁',
'新',
'苏',
'浙',
'赣',
'鄂',
'桂',
'甘',
'晋',
'蒙',
'陕',
'吉',
'闽',
'贵',
'粤',
'青',
'藏',
'川',
'宁',
'琼'
];
const VALID_CHARS = /^[A-HJ-NP-Z0-9]$/i;
const props = defineProps<{
modelValue: string;
disabled?: boolean;
required?: boolean;
}>();
const emit = defineEmits<{
'update:modelValue': [value: string];
'change': [value: string];
}>();
const inputRefs = ref<HTMLInputElement[]>([]);
const activeIndex = ref(-1);
const chars = ref<string[]>(Array(8).fill(''));
const hasError = ref(false);
const errorMessage = ref('');
// 外部值同步
watch(
() => props.modelValue,
(val) => {
const currentValue = chars.value.join('').trim();
if (val !== currentValue) {
const newChars = Array(8).fill('');
if (val) {
val
.toUpperCase()
.split('')
.forEach((c, i) => {
if (i < 8) newChars[i] = c;
});
}
chars.value = newChars;
}
},
{ immediate: true }
);
// 内部值同步到外部
watch(
chars,
() => {
const value = chars.value.join('').trim();
emit('update:modelValue', value);
emit('change', value);
// 输入时清除错误
clearError();
},
{ deep: true }
);
function onInput(index: number) {
const char = chars.value[index].toUpperCase();
if (index === 0 && !PROVINCES.includes(char)) {
chars.value[index] = '';
return;
}
if (index > 0 && !VALID_CHARS.test(char)) {
chars.value[index] = '';
return;
}
chars.value[index] = char;
if (char && index < 7) {
nextTick(() => inputRefs.value[index + 1]?.focus());
}
}
function onKeydown(e: KeyboardEvent, index: number) {
if (e.key === 'Backspace' && !chars.value[index] && index > 0) {
e.preventDefault();
nextTick(() => {
inputRefs.value[index - 1]?.focus();
inputRefs.value[index - 1]?.select();
});
}
if (e.key === 'ArrowLeft' && index > 0) {
e.preventDefault();
inputRefs.value[index - 1]?.focus();
}
if (e.key === 'ArrowRight' && index < 7) {
e.preventDefault();
inputRefs.value[index + 1]?.focus();
}
}
function onPaste(e: ClipboardEvent) {
e.preventDefault();
const text = e.clipboardData?.getData('text')?.trim().toUpperCase() || '';
if (!text) return;
const newChars = Array(8).fill('');
text.split('').forEach((c, i) => {
if (i >= 8) return;
if (i === 0 && PROVINCES.includes(c)) newChars[i] = c;
if (i > 0 && VALID_CHARS.test(c)) newChars[i] = c;
});
chars.value = newChars;
const lastIndex = Math.min(text.length - 1, 6);
nextTick(() => inputRefs.value[lastIndex + 1]?.focus());
}
function onBlur() {
setTimeout(() => {
const hasFocus = inputRefs.value.some((input) => document.activeElement === input);
if (!hasFocus) {
activeIndex.value = -1;
}
}, 10);
}
// ✅ 手动验证方法(核心)
function validate(): { valid: boolean; message?: string } {
const value = chars.value.join('').trim();
// 必填验证
if (props.required && !value) {
hasError.value = true;
errorMessage.value = '请输入车牌号';
return { valid: false, message: errorMessage.value };
}
// 格式验证
if (value && !isValidPlate(value)) {
hasError.value = true;
errorMessage.value = '请输入正确的车牌号格式';
return { valid: false, message: errorMessage.value };
}
// 验证通过
clearError();
return { valid: true };
}
// 车牌格式验证(内置,不依赖外部工具)
function isValidPlate(plate: string): boolean {
return LicensePlateValidator.isValidPlate(plate);
}
// 清除错误
function clearError() {
hasError.value = false;
errorMessage.value = '';
}
// 暴露方法
defineExpose({
focus: () => nextTick(() => inputRefs.value[0]?.focus()),
clear: () => {
chars.value = Array(8).fill('');
},
validate,
clearError
});
</script>
<style scoped>
.license-plate-input {
display: flex;
flex-direction: column;
gap: 4px;
}
.plate_line {
display: flex;
align-items: center;
gap: 6px;
}
.plate-item {
width: 24px;
height: 24px;
line-height: 24px;
border: 1px solid #dcdfe6;
border-radius: 4px;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.plate-item.active {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
.plate-item.error {
border-color: #f56c6c;
}
.plate-item.disabled {
background-color: #f5f7fa;
cursor: not-allowed;
}
.plate-item input {
width: 100%;
height: 100%;
border: none;
text-align: center;
font-size: 15px;
font-weight: 500;
background: transparent;
outline: none;
}
.plate-error {
color: #f56c6c;
font-size: 12px;
line-height: 1.5;
margin-top: 2px;
}
</style>

View File

@@ -52,7 +52,7 @@
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
import { FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
import UserSelect from '@/components/UserSelect';
import UserSelect from '@/components/UserSelect/index.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { getTask, taskOperation, currentTaskAllUser, terminationTask } from '@/api/workflow/task';
const props = defineProps({
@@ -91,7 +91,9 @@ const task = ref<FlowTaskVO>({
nodeRatio: undefined,
version: undefined,
applyNode: undefined,
buttonList: []
buttonList: [],
businessCode: '',
businessTitle: ''
});
const open = (taskId: string) => {

View File

@@ -152,7 +152,7 @@ import {
currentTaskAllUser,
getNextNodeList
} from '@/api/workflow/task';
import UserSelect from '@/components/UserSelect';
import UserSelect from '@/components/UserSelect.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { FlowCopyVo, FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';

View File

@@ -161,15 +161,15 @@ const toggleDark = () => useToggle(isDark);
/** 菜单导航设置 */
watch(
() => navType,
navType,
(val: string) => {
if (val.value === NavTypeEnum.TOP) {
if (val === NavTypeEnum.TOP) {
appStore.toggleSideBarHide(true);
permissionStore.setSidebarRouters(permissionStore.defaultRoutes as any);
} else if (val.value === NavTypeEnum.LEFT) {
} else if (val === NavTypeEnum.LEFT) {
appStore.toggleSideBarHide(false);
permissionStore.setSidebarRouters(permissionStore.defaultRoutes as any);
} else if (val.value === NavTypeEnum.MIX) {
} else if (val === NavTypeEnum.MIX) {
appStore.toggleSideBarHide(false);
}
},

View File

@@ -36,7 +36,7 @@ const sideTheme = computed(() => settingsStore.sideTheme);
// 获取Logo背景色
const getLogoBackground = computed(() => {
if (settingsStore.isDark) {
if (settingsStore.dark) {
return 'var(--sidebar-bg)';
}
if (settingsStore.navType == NavTypeEnum.TOP) {
@@ -47,7 +47,7 @@ const getLogoBackground = computed(() => {
// 获取Logo文字颜色
const getLogoTextColor = computed(() => {
if (settingsStore.isDark) {
if (settingsStore.dark) {
return 'var(--sidebar-text)';
}
if (settingsStore.navType == NavTypeEnum.TOP) {

View File

@@ -72,10 +72,10 @@ const init = async () => {
}
const data: LoginData = {
socialCode: code,
socialState: state,
// socialCode: code,
// socialState: state,
tenantId: tenantId,
source: source,
// source: source,
clientId: import.meta.env.VITE_APP_CLIENT_ID,
grantType: 'social'
};

View File

@@ -11,7 +11,7 @@ import { useTagsViewStore } from '@/store/modules/tagsView';
const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref<ElScrollbarInstance>();
const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef);
const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as HTMLElement);
onMounted(() => {
scrollWrapper.value?.addEventListener('scroll', emitScroll, true);

View File

@@ -12,58 +12,58 @@
</template>
<script setup>
import SidebarItem from '../Sidebar/SidebarItem'
import {useAppStore} from '@/store/modules/app'
import {useSettingsStore} from '@/store/modules/settings'
import {usePermissionStore} from '@/store/modules/permission'
import SidebarItem from '../Sidebar/SidebarItem';
import { useAppStore } from '@/store/modules/app';
import { useSettingsStore } from '@/store/modules/settings';
import { usePermissionStore } from '@/store/modules/permission';
const route = useRoute()
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
const route = useRoute();
const appStore = useAppStore();
const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore();
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
const theme = computed(() => settingsStore.theme)
const device = computed(() => appStore.device)
const sidebarRouters = computed(() => permissionStore.sidebarRouters);
const theme = computed(() => settingsStore.theme);
const device = computed(() => appStore.device);
const activeMenu = computed(() => {
const { meta, path } = route
const { meta, path } = route;
if (meta.activeMenu) {
return meta.activeMenu
return meta.activeMenu;
}
return path
})
return path;
});
const visibleNumber = ref(5)
const visibleNumber = ref(5);
const topMenus = computed(() => {
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(0, visibleNumber.value)
})
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(0, visibleNumber.value);
});
const moreRoutes = computed(() => {
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(visibleNumber.value, sidebarRouters.value.length - visibleNumber.value)
})
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(visibleNumber.value, sidebarRouters.value.length - visibleNumber.value);
});
function setVisibleNumber() {
let width = document.body.getBoundingClientRect().width
let width = document.body.getBoundingClientRect().width;
if (width >= 1000) {
width -= 500
width -= 500;
}
visibleNumber.value = parseInt(width / 3 / 85)
visibleNumber.value = parseInt(width / 3 / 85);
}
onMounted(() => {
window.addEventListener('resize', setVisibleNumber)
})
window.addEventListener('resize', setVisibleNumber);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', setVisibleNumber)
})
window.removeEventListener('resize', setVisibleNumber);
});
onMounted(() => {
setVisibleNumber()
})
setVisibleNumber();
});
</script>
<style lang="scss">
/* menu item */
#app .topbar-menu.el-menu--horizontal .el-sub-menu__title, #app .topbar-menu.el-menu--horizontal .el-menu-item {
#app .topbar-menu.el-menu--horizontal .el-sub-menu__title,
#app .topbar-menu.el-menu--horizontal .el-menu-item {
padding: 0 10px !important;
}
@@ -76,7 +76,10 @@ onMounted(() => {
margin: 0 10px !important;
}
.el-sub-menu.is-active .svg-icon, .el-menu-item.is-active .svg-icon + span, .el-sub-menu.is-active .svg-icon + span, .el-sub-menu.is-active .el-sub-menu__title span {
.el-sub-menu.is-active .svg-icon,
.el-menu-item.is-active .svg-icon + span,
.el-sub-menu.is-active .svg-icon + span,
.el-sub-menu.is-active .el-sub-menu__title span {
color: v-bind(theme);
}
@@ -85,7 +88,7 @@ onMounted(() => {
float: left;
line-height: 50px !important;
color: #303133 !important;
margin: 0 15px -3px!important;
margin: 0 15px -3px !important;
}
/* topbar more arrow */
@@ -97,7 +100,8 @@ onMounted(() => {
}
/* menu__title el-menu-item */
.topbar-menu.el-menu--horizontal .el-sub-menu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
.topbar-menu.el-menu--horizontal .el-sub-menu__title,
.topbar-menu.el-menu--horizontal .el-menu-item {
height: 60px;
}
</style>

View File

@@ -89,33 +89,45 @@ export const constantRoutes: RouteRecordRaw[] = [
{
path: '',
component: Layout,
redirect: '/index',
// redirect: '/index',
redirect: import.meta.env.VITE_LOGIN_BACK_URL,
// redirect: '/equipment/WaterDevice',
children: [
{
// {
// path: '/index',
// component: () => import('@/views/workbench/index2.vue'),
// name: 'Index',
// // hidden: true,
// meta: { title: '工作台', icon: 'dashboard', affix: true }
// }
]
}
// {
// path: '/user',
// component: Layout,
// hidden: true,
// redirect: 'noredirect',
// children: [
// {
// path: 'profile',
// component: () => import('@/views/system/user/profile/index.vue'),
// name: 'Profile',
// meta: { title: '个人中心', icon: 'user' }
// }
// ]
// }
];
if (import.meta.env.VITE_APP_SHOW_WORK_PLATFORM === 'true') {
const find = constantRoutes.find((item) => item.path === '');
if (find) {
find.children?.push({
path: '/index',
component: () => import('@/views/workbench/index2.vue'),
name: 'Index',
// hidden: true,
meta: { title: '工作台', icon: 'dashboard', affix: true }
});
}
]
},
{
path: '/user',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'profile',
component: () => import('@/views/system/user/profile/index.vue'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' }
}
]
}
];
}
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes: RouteRecordRaw[] = [];

View File

@@ -2,6 +2,7 @@ import { LanguageEnum } from '@/enums/LanguageEnum';
import { NavTypeEnum } from '@/enums/NavTypeEnum';
const setting: DefaultSettings = {
topNav: false,
/**
* 网页标题
*/

View File

@@ -163,6 +163,21 @@ export const useHouseStore = defineStore(
});
});
};
// 根据房屋号,查询房屋
async function getHouseBybuildingNo(houseName: string) {
currentHouseInfoList.value = null;
// 获取当前 楼栋的所有信息
const res3 = await getHouselistAPI(currentBuildingInfo.value.id, houseName);
currentHouseInfoList.value = res3.data;
floorslist.value = null;
floorslist.value = new Set();
// 得到所有的有房屋的 楼层 数组
currentHouseInfoList.value.units.forEach((item) => {
item.floors.forEach((floor) => {
floorslist.value.add(floor.floorNo);
});
});
}
// 切换楼栋
const switchBuilding = (buildingId: string) => {
if (!buildingId) return;
@@ -218,9 +233,10 @@ export const useHouseStore = defineStore(
// 房屋 树状结构类型
const getCurrentVilageTreeHouse = async () => {
const villageid = localStorage.getItem('villageid');
try {
treedata.value = [];
const res = await getVillageTree(currentVilageInfo.value.villageId);
const res = await getVillageTree(villageid);
treedata.value = convertBuildingToTree(res.data.buildings || []);
return treedata.value;
} catch (err) {
@@ -239,6 +255,7 @@ export const useHouseStore = defineStore(
buildingtype,
buildingtypelist,
treedata,
getHouseBybuildingNo,
updateVilageinfo,
updateCureentBuilding,
getviliageinfo,

10
src/types/global.d.ts vendored
View File

@@ -17,10 +17,14 @@ declare global {
}
/** 地图 */
interface Window {
// 高德全局对象
_AMapSecurityConfig?: {
securityJsCode: string;
};
// 天地图全局对象
T: any;
}
/**
* 弹窗属性
*/
@@ -65,7 +69,7 @@ declare global {
declare interface DictDataOption {
label: string;
value: string;
elTagType?: ElTagType;
elTagType?: ElTagType | '' | 'default';
elTagClass?: string;
}
@@ -95,6 +99,10 @@ declare global {
pageSize: number;
}
declare interface LayoutSetting {
/**
* 是否显示顶部导航
*/
topNav: boolean; // 新增此字段
/**
* 默认布局
*/

View File

@@ -64,3 +64,153 @@ export type RegType = keyof typeof reg;
export function validator(value: string | number, regType: RegType): boolean {
return reg[regType].test(String(value));
}
/**
* 车牌验证工具类
* 支持传统燃油、新能源、军警、武警、使馆、临时、港澳等全量车牌
*/
export class LicensePlateValidator {
/**
* 省份简称正则片段
*/
private static readonly PROVINCE = '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]';
/**
* 使领馆前缀
*/
private static readonly EMBASSY_PREFIX = '[使领]';
/**
* 验证传统燃油车牌 (蓝牌/黄牌)
* 格式: 省份 + 字母 + 5位字符
* 示例: 京A·12345, 粤B·88888
*/
static isTraditionalPlate(plate: string): boolean {
// 包含普通民用以及使领馆非新能源部分(如果使领馆归为此类,通常使领馆有单独规则,这里仅指普通民用)
// 注意:使领馆车牌通常格式特殊,见 isEmbassyPlate
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][A-HJ-NP-Z0-9]{5}$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证新能源车牌 (绿牌)
* 小型: 省份 + 字母 + D/F + 4位数字/字母 或 省份 + 字母 + 5位数字/字母 + D/F
* 大型: 省份 + 字母 + 5位数字/字母 + D/F (通常最后一位是D/F)
* 示例: 京AD12345, 京A12345D, 沪AF12345
*/
static isNewEnergyPlate(plate: string): boolean {
// 小型新能源第2位或第7位是D/F
// 大型新能源最后一位是D/F (且总长8位)
// 综合正则:
// 1. [省份][A-HJ-NP-Z][D-F][0-9A-HJ-NP-Z]{4} (小型D/F在第2位)
// 2. [省份][A-HJ-NP-Z][0-9A-HJ-NP-Z]{4}[D-F] (小型/大型D/F在最后)
// 3. [省份][A-HJ-NP-Z][0-9A-HJ-NP-Z][D-F][0-9A-HJ-NP-Z]{3} (较少见但某些早期试点或特殊编排可能存在标准通常只认1和2)
// 更严谨的标准新能源正则 (GA 804-2018):
// 小型: [省份][A-HJ-NP-Z][D-F][0-9A-HJ-NP-Z]{4} | [省份][A-HJ-NP-Z][0-9A-HJ-NP-Z]{4}[D-F]
// 大型: [省份][A-HJ-NP-Z][0-9A-HJ-NP-Z]{5}[D-F] (其实包含在上面第二种情况的子集里只要保证总长8位)
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z](?:[D-F][0-9A-HJ-NP-Z]{4}|[0-9A-HJ-NP-Z]{5}[D-F])$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证警车车牌
* 格式: 省份 + 字母 + 4位数字 + 警
* 示例: 京A·1234警
*/
static isPolicePlate(plate: string): boolean {
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][0-9]{4}警$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证教练车牌
* 格式: 省份 + 字母 + 4位数字 + 学
* 示例: 京A·1234学
*/
static isCoachPlate(plate: string): boolean {
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][0-9]{4}学$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证武警车牌 (2013式)
* 格式: WJ + 省份/部门简称 + 5位字符
* 示例: WJ·京12345, WJ·12345X, WJ·B12345
* 注意WJ后紧跟的可能是省份简称或部门字母
*/
static isArmedPolicePlate(plate: string): boolean {
// WJ + (省份简称 | 部门字母) + 5位字符
// 部门字母包括: 内边消水电林通海等,这里简化处理,允许字母或省份
const reg = /^WJ(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]|[A-Z])[0-9A-HJ-NP-Z]{5}$/;
return reg.test(plate.toUpperCase());
}
/**
* 验证军队车牌 (2012式)
* 格式: 军种字母 + · + 5位字符 (实际输入常不带点)
* 军种: V(军委), K(空军), H(海军), B(北京军区), S(沈阳), L(兰州), J(济南), N(南京), G(广州), C(成都) 等
* 示例: V·12345, K·A1234
*/
static isMilitaryPlate(plate: string): boolean {
// 简化版首字母为大写排除I,O后跟5位字符
// 更严谨应限制首字母为特定军种代号,但考虑到军改变化,此处做通用校验
// 2012式军牌格式XY·12345 (X为军种/大区Y为单位)
// 常见前缀: V, K, H, B, S, L, J, N, G, C, M, P, O, A, E 等
const reg = /^[VKBHSLJNGCMPEOA][A-HJ-NP-Z0-9][0-9A-HJ-NP-Z]{4}$/;
return reg.test(plate.toUpperCase());
}
/**
* 验证使馆/领馆车牌
* 格式: 使/领 + 3位数字 + 3位数字 (共6位数字或 使·123456)
* 示例: 使·123456, 领·123456
*/
static isEmbassyPlate(plate: string): boolean {
const reg = /^[使领][0-9]{6}$/;
return reg.test(plate);
}
/**
* 验证港澳入出境车 (粤Z牌)
* 格式: 粤Z + 4位字符 + 港/澳
* 示例: 粤Z·1234港, 粤Z·1234澳
*/
static isHKMacauPlate(plate: string): boolean {
const reg = /^粤Z[A-HJ-NP-Z0-9]{4}[港澳]$/;
return reg.test(plate.toUpperCase());
}
/**
* 验证临时车牌
* 格式: 省份 + 字母 + 5位字符 + 临 / 超
* 示例: 京A·12345临, 沪B·12345超
*/
static isTemporaryPlate(plate: string): boolean {
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][0-9A-HJ-NP-Z]{5}[临超]$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证所有合法车牌(全量)
* @param plate 车牌号码
*/
static isValidPlate(plate: string): boolean {
if (!plate || typeof plate !== 'string') return false;
const upperPlate = plate.toUpperCase().replace(/[·\s-]/g, ''); // 去除分隔符和空格
return (
this.isTraditionalPlate(upperPlate) ||
this.isNewEnergyPlate(upperPlate) ||
this.isPolicePlate(upperPlate) ||
this.isCoachPlate(upperPlate) ||
this.isArmedPolicePlate(upperPlate) ||
this.isMilitaryPlate(upperPlate) ||
this.isEmbassyPlate(upperPlate) ||
this.isHKMacauPlate(upperPlate) ||
this.isTemporaryPlate(upperPlate)
);
}
}

117
src/utils/exportZip.ts Normal file
View File

@@ -0,0 +1,117 @@
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
// 假设使用的是 Element Plus请根据实际 UI 库调整导入
import { ElMessage } from 'element-plus';
/**
* 图片数据接口
*/
interface ImageItem {
name: string;
url: string;
}
/**
* 批量导出图片并生成 ZIP
* @param imageList 图片数组
* @param zipName 压缩包名称
* @param concurrency 并发请求数,默认 5避免浏览器请求过多被拦截
*/
export async function exportImagesToZip(imageList: ImageItem[], zipName: string = 'images', concurrency: number = 5) {
if (!imageList || imageList.length === 0) {
ElMessage.warning('没有可导出的图片');
return;
}
const zip = new JSZip();
try {
// 使用并发控制处理图片获取
const tasks = imageList.map((item) => () => processImageItem(item, zip));
// 执行并发任务
await runWithConcurrency(tasks, concurrency);
ElMessage.success('导出完成');
// 生成 ZIP 并下载
const content = await zip.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: { level: 6 }
});
saveAs(content, `${zipName}.zip`);
} catch (err) {
console.error('ZIP 导出失败:', err);
ElMessage.error('图片导出失败,请重试');
}
}
/**
* 处理单个图片项:获取 Blob 并添加到 Zip
*/
async function processImageItem(item: ImageItem, zip: JSZip): Promise<void> {
try {
// 清理文件名,防止非法字符或扩展名缺失
const safeName = sanitizeFileName(item.name);
const blob = await getImageBlobViaCanvas(item.url);
zip.file(`${safeName}.png`, blob, { binary: true });
} catch (error) {
console.warn(`图片加载失败: ${item.url}`, error);
throw error; // 抛出错误以便外层统计或处理
}
}
/**
* 通过 Canvas 获取图片 Blob (解决 OSS 跨域 fetch 问题)
* 注意:这要求 OSS _bucket_ 配置了 CORS 允许 '*' 或你的域名,且允许 GET 方法。
* 如果 OSS 完全禁止跨域,此方法也会失败,但通常公共读 OSS 是允许 img 标签跨域显示的。
*/
async function getImageBlobViaCanvas(url: string): Promise<Blob> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.blob();
}
/**
* 简单的并发控制执行器
*/
async function runWithConcurrency(tasks: (() => Promise<void>)[], limit: number): Promise<void> {
const results: Promise<void>[] = [];
let index = 0;
async function worker() {
while (index < tasks.length) {
const currentIndex = index++;
const task = tasks[currentIndex];
try {
await task();
} catch (e) {
// 这里可以选择忽略单个失败,或者累计错误
console.error(`Task ${currentIndex} failed`, e);
}
}
}
// 创建 limit 个工人
const workers = Array(Math.min(limit, tasks.length)).fill(null).map(worker);
await Promise.all(workers);
}
/**
* 清理文件名,确保合法且唯一(简单版)
*/
function sanitizeFileName(name: string): string {
// 移除非法字符,保留字母、数字、中文、下划线、点、横杠
let cleanName = name.replace(/[<>:"/\\|?*]/g, '_');
// 如果名字为空或只有扩展名,给一个默认名
if (!cleanName || cleanName.startsWith('.')) {
cleanName = `image_${Date.now()}_${Math.random().toString(36).substr(2, 9)}${cleanName}`;
}
return cleanName;
}

View File

@@ -58,22 +58,6 @@ export function convertBuildingToTree(buildings: any[]): Tree[] {
});
}
// 与业主关系
export const ownerRelationshiplist = [
{
label: '业主',
value: 0
},
{
label: '亲属',
value: 1
},
{
label: '租户',
value: 2
}
];
interface Tree {
label: string;
value: number;
@@ -120,8 +104,6 @@ export function getHousePathById(treeData: Tree[], houseId: string | number, res
return path;
}
export const houseUselist = ['普通住宅', '商业用地', '商住两用', '其他'];
// 对字符串 图片地址,处理
export function splitStringUrl(str: string, splitLabel: string = ',') {
const list = str.split(splitLabel);
@@ -149,25 +131,3 @@ export function hasFormData(formData: FormData): boolean {
}
return false;
}
/** 方法渲燃模板数据 */
type dataType = {
'label': string;
'value': string;
'elTagType': string;
'elTagClass': string;
};
/**
* 通用:根据 value 从字典数组中获取 label
* @param dict 字典数组
* @param value 要匹配的值
* @returns 匹配到的label无则返回原value
*/
export function getDictLabel(dict: Ref<dataType>, value: number): string {
// 空值直接返回
if (value == null) return '-';
// 统一转字符串匹配
const item = (dict.value || []).find((i) => String(i.value) === String(value));
return item ? item.label : String(value);
}

View File

@@ -34,14 +34,15 @@ const service = axios.create({
clarifyTimeoutError: true
}
});
// 不需要小区 ID 的接口白名单(支持 * 通配符)
const whiteVillageId = [
'/village',
'/village/*',
'/waterDevice/generateBarCode', // 水表 条码
'/electricityDevice/generateBarCode', // 电表 条码
'/payRecordList/payRecordList/list'
'/payRecordList/payRecordList/list',
'/warehouse/*'
// 以后直接在这里加新地址,支持 /xxx/*、/xxx/xxx 写法
];
@@ -202,9 +203,6 @@ service.interceptors.response.use(
useUserStore().logout().then(() => {
router.replace({
path: '/login',
query: {
redirect: encodeURIComponent(router.currentRoute.value.fullPath || '/')
}
})
});
}).catch(() => {

198
src/utils/weather.ts Normal file
View File

@@ -0,0 +1,198 @@
const weatherIcons: Record<number, string> = {
100: '☀️',
101: '⛅',
102: '🌤️',
103: '⛅',
104: '☁️',
150: '🌙',
151: '🌙',
152: '🌙',
153: '🌙',
300: '🌦️',
301: '🌧️',
302: '⛈️',
303: '⛈️',
304: '⛈️',
305: '🌧️',
306: '🌧️',
307: '🌧️',
308: '🌧️',
309: '🌧️',
310: '🌊',
311: '🌊',
312: '🌊',
313: '🧊',
314: '🌧️',
315: '🌧️',
316: '🌊',
317: '🌊',
318: '🌊',
350: '🌙',
351: '🌙',
399: '🌧️',
400: '🌨️',
401: '🌨️',
402: '❄️',
403: '❄️',
404: '🌨️',
405: '🌨️',
406: '🌨️',
407: '🌨️',
408: '🌨️',
409: '❄️',
410: '❄️',
456: '🌙',
457: '🌙',
499: '❄️',
500: '🌫️',
501: '🌫️',
502: '😶‍🌫️',
503: '💨',
504: '💨',
507: '🏜️',
508: '🏜️',
509: '🌫️',
510: '🌫️',
511: '😶‍🌫️',
512: '😶‍🌫️',
513: '😶‍🌫️',
514: '🌫️',
515: '🌫️',
800: '🌑',
801: '🌒',
802: '🌓',
803: '🌔',
804: '🌕',
805: '🌖',
806: '🌗',
807: '🌘',
900: '🥵',
901: '🥶',
999: '❓',
1001: '🌀',
1002: '🌪️',
1003: '🌊',
1004: '❄️',
1005: '🥶',
1006: '💨',
1007: '🏜️',
1008: '🧊',
1009: '🌡️',
1010: '🥵',
1011: '🌾',
1012: '💨',
1013: '🏔️',
1014: '⚡',
1015: '🧊',
1016: '🥶',
1017: '🌫️',
1018: '✈️',
1019: '😶‍🌫️',
1020: '⛈️',
1021: '🧊',
1022: '☀️',
1023: '🌊',
1024: '🥵',
1025: '🔥',
1026: '🔥',
1027: '🧊',
1028: '🛰️',
1029: '😶‍🌫️',
1030: '🌨️',
1031: '⛈️',
1032: '☀️',
1033: '❄️',
1034: '🥶',
1035: '🌧️',
1036: '🌊',
1037: '⛰️',
1038: '🌧️',
1039: '🌡️',
1040: '❄️',
1041: '🔥',
1042: '🏥',
1043: '⛈️',
1044: '🏫',
1045: '🏭',
1046: '🚢',
1047: '💨',
1048: '🌡️',
1049: '🌀',
1050: '🥶',
1051: '💨',
1052: '⛈️',
1053: '🌫️',
1054: '⚡',
1055: '🌀',
1056: '🌡️',
1057: '🧊',
1058: '⛈️',
1059: '🥶',
1060: '🌫️',
1061: '💨',
1062: '🌊',
1063: '🌧️',
1064: '🌧️',
1065: '🌫️',
1066: '🥵',
1067: '😶‍🌫️',
1068: '⚠️',
1069: '🏥',
1071: '🏥',
1072: '🏥',
1073: '🌊',
1074: '😶‍🌫️',
1075: '🏙️',
1076: '🌊',
1077: '🔥',
1078: '☀️',
1079: '🌾',
1080: '💨',
1081: '🧊',
1082: '🏥',
1084: '🔥',
1085: '⛈️',
1086: '🧊',
1087: '🌡️',
1088: '🌾',
1089: '🌾',
1201: '🌊',
1202: '🏙️',
1203: '⚠️',
1204: '⚠️',
1205: '🧊',
1206: '🌊',
1207: '🌊',
1208: '☀️',
1209: '🌊',
1210: '💧',
1211: '🌊',
1212: '🌊',
1213: '🏙️',
1214: '🌊',
1215: '☀️',
1216: '💧',
1217: '🌿',
1218: '⚠️',
1219: '🌊',
1221: '☀️',
1241: '⛰️',
1242: '⛰️',
1243: '⛰️',
1244: '⛰️',
1245: '⛰️',
1246: '⛰️',
1247: '🌋',
1248: '⛰️',
1249: '⛰️',
1250: '⛰️',
1251: '⛰️',
1271: '😶‍🌫️',
1272: '😶‍🌫️',
1273: '😶‍🌫️',
9999: '⚠️'
};
export const getWeatherIcon = (weatherCode: string): string => {
return weatherIcons[weatherCode] || weatherIcons[999];
};

View File

@@ -15,7 +15,7 @@
<div class="bullshit__info">
对不起您正在寻找的页面不存在尝试检查URL的错误然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容
</div>
<router-link to="/index" class="bullshit__return-home"> 返回首页 </router-link>
<router-link to="/" class="bullshit__return-home"> 返回首页 </router-link>
</div>
</div>
</div>

View File

@@ -123,7 +123,7 @@ const single = ref(true);
const multiple = ref(true);
const selectName = ref<Array<string>>([]);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dateRange = ref<[string, string]>(['', '']);
const defaultSort = ref<any>({ prop: 'loginTime', order: 'descending' });
const queryFormRef = ref<ElFormInstance>();

View File

@@ -143,7 +143,7 @@ const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dateRange = ref<[string, string]>(['', '']);
const defaultSort = ref<any>({ prop: 'operTime', order: 'descending' });
const operLogTableRef = ref<ElTableInstance>();

View File

@@ -138,7 +138,7 @@ const handleRegister = () => {
if (!err) {
const username = registerForm.value.username;
await ElMessageBox.alert('<span style="color: red; ">' + t('register.registerSuccess', { username }) + '</font>', '系统提示', {
app: undefined,
// app: undefined,
dangerouslyUseHTMLString: true,
type: 'success'
});

View File

@@ -28,11 +28,11 @@
</template>
</el-upload>
</el-form-item>
<el-form-item label="跳转链接" prop="jump_link">
<el-input v-model.trim="form.jump_link" placeholder="请输入跳转链接" />
<el-form-item label="跳转链接" prop="link">
<el-input v-model.trim="form.link" placeholder="请输入跳转链接" />
</el-form-item>
<el-form-item label="排序号" prop="order">
<el-input-number v-model.trim="form.order" placeholder="请输入" />
<el-form-item label="排序号" prop="byOrder">
<el-input-number v-model.trim="form.byOrder" placeholder="请输入" />
</el-form-item>
<el-form-item style="margin-top: 30px">
<el-button type="primary" @click="submitForm">提交</el-button>
@@ -91,8 +91,8 @@ const form = ref({
id: null,
name: '', // 分类id、
url: '', // 设备名称
jump_link: '', // 编号
order: null // 区域
link: '', // 编号
byOrder: 1 // 区域
});
const rules = ref({
name: [{ required: true, message: '请输入Banner图名称', trigger: 'blur' }],
@@ -108,8 +108,8 @@ function init() {
id: res.data.id,
name: res.data.name, // 分类id、
url: res.data.url, // 设备名称
jump_link: res.data.jump_link, // 编号
order: res.data.order // 区域
link: res.data.link, // 编号
byOrder: res.data.byOrder // 区域
};
});
}

View File

@@ -64,11 +64,17 @@
<el-table-column label="名称" align="center" prop="name" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-switch inactive-value="0" active-value="1" v-model="scope.row.status"></el-switch>
<!-- 0启用 1停用 -->
<el-switch
@change="(val: string) => handleSwitch(scope.row.id, val)"
inactive-value="0"
active-value="1"
v-model="scope.row.status"
></el-switch>
</template>
</el-table-column>
<el-table-column label="排序" align="center" prop="order" />
<el-table-column label="添加时间" align="center" prop="order" />
<el-table-column label="排序" align="center" prop="byOrder" />
<el-table-column label="添加时间" align="center" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['banner:banner:edit']">修改</el-button>
@@ -81,12 +87,12 @@
</el-card>
<!-- 添加或修改Banner图管理对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="bannerFormRef" :model="form" :rules="rules" label-width="80px">
<el-form ref="bannerFormRef" :model="form" label-width="80px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="排序" prop="order">
<el-input v-model="form.order" placeholder="请输入排序" />
<el-form-item label="排序" prop="byOrder">
<el-input v-model="form.byOrder" placeholder="请输入排序" />
</el-form-item>
<el-form-item label="小区id" prop="villageId">
<el-input v-model="form.villageId" placeholder="请输入小区id" />
@@ -103,7 +109,7 @@
</template>
<script setup name="Banner" lang="ts">
import { listBanner, getBanner, delBanner, addBanner, updateBanner } from '@/api/system/Banner';
import { listBanner, getBanner, delBanner, addBanner, updateBanner, switchBanner } from '@/api/system/Banner';
import { BannerVO, BannerQuery, BannerForm } from '@/api/system/Banner/type';
import { checkPermi } from '@/utils/permission';
@@ -130,7 +136,8 @@ const initFormData: BannerForm = {
id: undefined,
name: undefined,
status: undefined,
order: undefined,
byOrder: undefined,
link: undefined,
villageId: undefined
};
const data = reactive<PageData<BannerForm, BannerQuery>>({
@@ -140,9 +147,9 @@ const data = reactive<PageData<BannerForm, BannerQuery>>({
pageSize: 10,
name: undefined,
status: undefined,
order: undefined,
villageId: undefined,
params: {}
byOrder: undefined,
link: undefined,
villageId: undefined
}
});
@@ -243,6 +250,12 @@ const handleExport = () => {
`banner_${new Date().getTime()}.xlsx`
);
};
function handleSwitch(id: string, status: string) {
switchBanner(id, status).then(() => {
ElMessage.success('操作成功');
getList();
});
}
onMounted(() => {
getList();

View File

@@ -33,7 +33,7 @@
<div class="carinfo_item">
<div class="label">投诉时间</div>
<div class="main">{{ form.orderDate }}</div>
<div class="main">{{ form.createTime }}</div>
</div>
<div class="carinfo_item">
@@ -100,7 +100,8 @@ const form = ref({
'status': '1', // 投诉状态 0待处理 1已处理
'handleId': '',
'handleName': '',
'handleContent': ''
'handleContent': '',
'createTime': null
});
const rules = {
status: [{ required: true, message: '选择投诉处理状态', trigger: 'blur' }]

View File

@@ -28,7 +28,7 @@
<el-col :span="2"></el-col>
<el-col :span="11">
<el-form-item label="绑定车牌号" prop="carNum">
<el-input v-model.number="form.carNum" placeholder="请输入车牌号" />
<LicensePlateInput ref="carNumInputRef" required v-model="form.carNum" @change="() => handleValidate()" />
</el-form-item>
</el-col>
</el-row>
@@ -92,7 +92,7 @@
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { addMonthCard, getMonthCard, updateMonthCard } from '@/api/system/MonthCard';
import { validator } from '@/utils/Reg';
import { LicensePlateValidator, validator } from '@/utils/Reg';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_pay_method } = toRefs<any>(proxy?.useDict('com_pay_method'));
@@ -139,9 +139,18 @@ const rules = ref({
carNum: [
{ required: true, message: '请输入车牌号', trigger: 'blur' },
{
validator: (_, value) => validator(value, 'PlateNumber'),
message: '请输入正确的车牌号',
trigger: 'blur'
validator: (rule: any, value: string, callback: any) => {
if (!value) {
callback();
return;
}
if (!LicensePlateValidator.isValidPlate(value)) {
callback(new Error('请输入正确的车牌号格式'));
return;
}
callback();
},
trigger: 'blur' // ✅ 只在失焦时验证
}
],
startTime: [{ required: true, message: '请选择有效日期', trigger: 'blur' }],
@@ -154,7 +163,12 @@ const rules = ref({
}
]
});
function handleValidate() {
formRef.value.validateField('carNum');
}
const userid = ref(null);
if (route.query.id) {
userid.value = route.query.id;
getMonthCard(userid.value).then((res) => {

View File

@@ -1,6 +1,6 @@
<template>
<div class="p-2">
<Pageheader title="公告管理"></Pageheader>
<div class="p-2 h-full flex flex-col">
<Pageheader></Pageheader>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
@@ -17,7 +17,7 @@
</div>
</transition>
<el-card shadow="never">
<el-card shadow="never" class="flex-1">
<template #header>
<el-row :gutter="10" class="mb8">
<!-- <el-col :span="1.5">
@@ -199,3 +199,9 @@ onMounted(() => {
getList();
});
</script>
<style lang="scss" scoped>
.el-table {
height: calc(100% - 80px);
}
</style>

View File

@@ -41,7 +41,8 @@
<el-row>
<el-col :span="11">
<el-form-item label="车牌号" prop="carNum">
<el-input v-model.number.trim="form.carNum" placeholder="请输入车牌号" />
<!-- <el-input v-model.number.trim="form.carNum" placeholder="请输入车牌号" /> -->
<LicensePlateInput ref="carNumInputRef" required v-model="form.carNum" @change="() => handleValidate()" />
</el-form-item>
</el-col>
<el-col :span="2"></el-col>
@@ -108,10 +109,11 @@
<script setup lang="ts">
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import LicensePlateInput from '@/components/LicensePlateInput/index.vue';
import { listArea } from '@/api/system/carArea';
import { addParkingCost, getParkingCost, updateParkingCost } from '@/api/system/ParkingCost';
import { listParking } from '@/api/system/parking';
import { validator } from '@/utils/Reg';
import { LicensePlateValidator, validator } from '@/utils/Reg';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_pay_method } = toRefs<any>(proxy?.useDict('com_pay_method'));
const { com_parking_charge } = toRefs<any>(proxy?.useDict('com_parking_charge'));
@@ -130,7 +132,7 @@ const form = ref({
carNum: null, // 车牌号
startTime: '', // 开始时间
endTime: '', // 结束时间
cost: '', // 费用
cost: null, // 费用
payMethod: '', // 支付方式
payerStatus: '0', // 缴纳人状态
remark: '' // 备注
@@ -156,9 +158,18 @@ const rules = ref({
carNum: [
{ required: true, message: '请绑定车牌号', trigger: 'blur' },
{
validator: (_, value) => validator(value, 'PlateNumber'),
message: '请请绑定车牌号',
trigger: 'blur'
validator: (rule: any, value: string, callback: any) => {
if (!value) {
callback();
return;
}
if (!LicensePlateValidator.isValidPlate(value)) {
callback(new Error('请输入正确的车牌号格式'));
return;
}
callback();
},
trigger: 'blur' // ✅ 只在失焦时验证
}
],
startTime: [{ required: true, message: '请选择有效日期', trigger: 'blur' }],
@@ -172,6 +183,9 @@ const rules = ref({
}
]
});
function handleValidate() {
formRef.value.validateField('carNum');
}
const userid = ref(null);
// 区域

View File

@@ -61,7 +61,7 @@
</el-table-column>
<el-table-column label="缴纳人" align="center" prop="payerName" />
<el-table-column label="车牌号" align="center" prop="carNum" />
<el-table-column label="停车时长" align="center" width="180">
<el-table-column label="停车时长" align="center" width="100">
<template #default="scope">
<span>{{ formatTimeDiff(scope.row.startTime, scope.row.endTime) }}</span>
</template>
@@ -76,7 +76,8 @@
</el-table-column>
<el-table-column label="支付方式" align="center" prop="payMethod">
<template #default="scope">
<dict-tag :options="com_pay_method" :value="scope.row.payMethod" />
<dict-tag v-if="scope.row.payMethod" :options="com_pay_method" :value="scope.row.payMethod" />
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
@@ -108,8 +109,8 @@ const { com_pay_method } = toRefs<any>(proxy?.useDict('com_pay_method'));
const router = useRouter();
interface ParkingCostType extends ParkingCostVO {
areaName: string;
parkingName: string;
areaName?: string;
parkingName?: string;
}
const parkingCostList = ref<ParkingCostType[]>([]);
const buttonLoading = ref(false);

View File

@@ -31,10 +31,10 @@
<!-- 填空题/多行文本展示 -->
<div v-else-if="q.type === 'input'" class="input-placeholder">
<el-input v-model="q.placeholder" placeholder="用户输入区域" />
<el-input disabled placeholder="用户输入区域" />
</div>
<div v-else-if="q.type === 'textarea'" class="input-placeholder">
<el-input type="textarea" v-model="q.placeholder" placeholder="用户输入区域" />
<el-input disabled type="textarea" placeholder="用户输入区域" />
</div>
</div>
</div>
@@ -90,7 +90,8 @@ function init() {
getQuestionnaire(viewid.value).then((res) => {
viewData.value = {
...viewData.value,
...res.data
...res.data,
content: [] as QuestionComponentItem[]
};
if (typeof res.data.content === 'string') {
viewData.value.content = JSON.parse(res.data.content) as QuestionComponentItem[];

View File

@@ -82,9 +82,8 @@ import QuestionnaireEdit from './components/QuestionnaireEdit.vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { Delete, DocumentAdd } from '@element-plus/icons-vue';
import { getQuestionnaire } from '../test/api';
import { QuestionnaireForm, QuestionnaireVO } from '@/api/system/Questionnaire/type';
import { addQuestionnaire, updateQuestionnaire } from '@/api/system/Questionnaire';
import { QuestionComponentnaireForm, QuestionnaireForm, QuestionnaireVO } from '@/api/system/Questionnaire/type';
import { addQuestionnaire, getQuestionnaire, updateQuestionnaire } from '@/api/system/Questionnaire';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_questionnaire_scope } = toRefs<any>(proxy?.useDict('com_questionnaire_scope'));
@@ -119,14 +118,13 @@ function delteUse(id: string | number) {
}
// ! 编辑问卷 ------------------------------------------------------------------------
const questionnaireEditRef = ref<InstanceType<typeof QuestionnaireEdit>>();
const editData = ref<QuestionnaireVO | null>(null);
const editData = ref<QuestionComponentnaireForm>(null);
// ! 路由参数 --------------------------------------------------------------------------------
const userid = ref(null);
if (route.query.id) {
userid.value = route.query.id;
handleEdit(userid.value);
} else {
handleAdd();
@@ -146,9 +144,9 @@ async function handleEdit(id: string | number) {
tableData.value = res.rows;
// 父组件自己调用接口获取数据
getQuestionnaire(id).then((res) => {
editData.value = res.data;
editData.value.content = JSON.parse(res.data.content);
questionnaireEditRef.value?.init(res.data);
editData.value = res.data as QuestionComponentnaireForm;
editData.value.content = JSON.parse(res.data.content as string);
questionnaireEditRef.value?.init(res.data as QuestionComponentnaireForm);
form.value = {
scope: res.data.scope,
residents: res.data.residents,
@@ -171,7 +169,7 @@ async function handleEdit(id: string | number) {
}
// 保存草稿
async function handleSave(data: QuestionnaireForm) {
async function handleSave(data: QuestionComponentnaireForm) {
try {
// 父组件自己调用接口提交
if (userid.value) {
@@ -202,7 +200,7 @@ async function handleSave(data: QuestionnaireForm) {
}
// 发布问卷
async function handlePublish(data: QuestionnaireForm) {
async function handlePublish(data: QuestionComponentnaireForm) {
try {
if (userid.value) {
await updateQuestionnaire({

View File

@@ -120,6 +120,8 @@ const initFormData: QuestionnaireForm = {
status: undefined,
scope: undefined,
residents: undefined,
startTime: undefined,
endTime: undefined,
remark: undefined
};
const data = reactive<PageData<QuestionnaireForm, QuestionnaireQuery>>({
@@ -127,19 +129,10 @@ const data = reactive<PageData<QuestionnaireForm, QuestionnaireQuery>>({
queryParams: {
pageNum: 1,
pageSize: 10,
villageId: undefined,
title: undefined,
description: undefined,
content: undefined,
status: undefined,
scope: undefined,
residents: undefined,
params: {}
title: undefined
},
rules: {
id: [{ required: true, message: '问卷ID不能为空', trigger: 'blur' }],
title: [{ required: true, message: '问卷标题不能为空', trigger: 'blur' }],
content: [{ required: true, message: '问卷题目结构不能为空', trigger: 'blur' }]
title: [{ required: true, message: '问卷标题不能为空', trigger: 'blur' }]
}
});

View File

@@ -14,7 +14,7 @@
<el-col :span="2"></el-col>
<el-col :span="11">
<el-form-item label="项目类型" prop="projectType">
<el-radio-group :disabled="form.projectLevel === '1'" v-model="form.projectType">
<el-radio-group :disabled="form.projectLevel === 1" v-model="form.projectType">
<el-radio v-for="item in com_repair_project_types" :key="item.id" :value="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
@@ -24,7 +24,7 @@
<el-col :span="11">
<el-form-item label="项目层级" prop="projectLevel">
<el-radio-group v-model="form.projectLevel">
<el-radio v-for="item in com_repair_project_level" :key="item.id" :value="item.value">{{ item.label }}</el-radio>
<el-radio v-for="item in com_repair_project_level" :key="item.id" :value="Number(item.value)">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@@ -37,7 +37,7 @@
</el-form-item>
</el-col>
</el-row>
<el-row v-if="form.projectLevel === '1'">
<el-row v-if="form.projectLevel === 1">
<el-col :span="11">
<el-form-item label="隶属父级" prop="email">
<el-select @change="handleChange" v-model="form.parentId" placeholder="请选择隶属父级" style="width: 240px">
@@ -74,7 +74,7 @@ const form = ref({
projectName: null, // 项目名称
parentId: '', // 父级项目id
projectType: '0', // 项目类型
projectLevel: '0', //项目层级
projectLevel: 0, //项目层级
status: '0' //状态
});
const rules = ref({
@@ -96,6 +96,14 @@ function handleChange(val) {
// != ==========================================
function init() {
form.value = {
id: null,
projectName: null, // 项目名称
parentId: '', // 父级项目id
projectType: '0', // 项目类型
projectLevel: 0, //项目层级
status: '0' //状态
};
listRepairProject().then((res) => {
const arr = res.rows.filter((ite) => ite.projectLevel === 0);
options.value = arr;
@@ -106,7 +114,7 @@ function init() {
form.value = {
...form.value,
...find,
projectLevel: String(find.projectLevel)
projectLevel: find.projectLevel
};
}
}

View File

@@ -150,7 +150,7 @@ import { ref } from 'vue';
import repairUser from '@/components/Holder/repairUser.vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { getRepair, updateRepair } from '@/api/system/Repair';
import { convertBuildingToTree, getHousePathById } from '@/utils/house';
import { getHousePathById } from '@/utils/house';
import { listRepairProject } from '@/api/system/RepairProject';
import { listResident } from '@/api/system/resident';
import { RepairVO } from '@/api/system/Repair/type';

View File

@@ -196,6 +196,8 @@ const initFormData: RepairProjectForm = {
const data = reactive<PageData<RepairProjectForm, RepairProjectQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
projectName: undefined,
parentId: undefined,
projectType: undefined,

View File

@@ -54,7 +54,7 @@
</el-table-column>
<el-table-column label="收费项目" align="center" prop="payType">
<template #default="scope">
<dict-tag :options="com_payrecord_pay_type" :value="scope.row.payType || ''"></dict-tag>
<dict-tag :options="com_payrecord_pay_type" :value="scope.row.payType"></dict-tag>
</template>
</el-table-column>
<el-table-column label="抄表周期" align="center" prop="recordMonth" width="180">
@@ -87,12 +87,88 @@
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改抄表周期明细对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
<el-form ref="payRecordListFormRef" :model="form" label-position="left">
<el-row>
<el-col :span="12">
<el-form-item label="抄表小区:" prop="recordId">
{{ form.villageName ?? '--' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="房屋号码:" prop="houseName"> {{ form.houseName ?? '--' }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="住户姓名:" prop="recordId">
{{ form.residentName ?? '--' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="住户编号:" prop="residentCode"> {{ form.residentCode ?? '--' }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="住户性别:" prop="recordId">
{{ form.gender === '1' ? '男' : '女' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号码:" prop="residentCode"> {{ form.phone ?? '--' }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="收费项目:" prop="recordId">
<dict-tag v-if="form.payType" :options="com_payrecord_pay_type" :value="form.payType"></dict-tag>
<span v-else>--</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="抄表月份:" prop="residentCode"> {{ form.recordMonth ?? '--' }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="收费起止日期:" prop="recordId">
{{ parseTime(form.startTime, '{y}-{m}-{d}') }} -- {{ parseTime(form.endTime, '{y}-{m}-{d}') }}
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="起始读数:" prop="startRead"> {{ form.startRead ?? '--' }} </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="终止读数:" prop="endRead"> {{ form.endRead ?? '--' }} </el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="本期用度:" prop="startRead"> {{ form.currentCost ?? '--' }} </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单价:" prop="price"> {{ form.price ?? '--' }} /</el-form-item>
</el-col>
</el-row>
<el-form-item label="费用合计:" prop="totalCost"> {{ form.totalCost ?? '--' }} </el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="PayRecord" lang="ts">
import { listPayRecord, getPayRecord, delPayRecord, addPayRecord, updatePayRecord } from '@/api/system/SdbPayRecord';
import { PayRecordVO, PayRecordQuery, PayRecordForm } from '@/api/system/SdbPayRecord/type';
import { PayRecordListForm } from '@/api/system/SdbPayRecordMain/type';
import { checkPermi } from '@/utils/permission';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -116,16 +192,29 @@ const dialog = reactive<DialogOption>({
title: ''
});
const initFormData: PayRecordForm = {
const initFormData: PayRecordListForm = {
id: undefined,
deviceType: undefined,
houseId: undefined,
recordId: undefined,
housenumname: undefined,
payType: undefined,
recordMonth: undefined,
startTime: undefined,
endTime: undefined
endTime: undefined,
currentCost: undefined,
totalCost: undefined,
deviceCode: undefined,
deviceType: undefined,
villageName: undefined,
houseName: undefined,
residentName: undefined,
gender: undefined,
phone: undefined,
startRead: undefined,
endRead: undefined,
price: undefined,
residentCode: undefined
};
const data = reactive<PageData<PayRecordForm, PayRecordQuery>>({
const data = reactive<PageData<PayRecordListForm, PayRecordQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
@@ -187,6 +276,27 @@ const handleMain = async (row?: PayRecordVO) => {
}
});
};
/** 修改按钮操作 */
const handleUpdate = async (id: string) => {
// const res = await getPayRecordList(id);
// Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '最新抄表详细';
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
const payRecordListFormRef = ref<ElFormInstance>();
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
payRecordListFormRef.value?.resetFields();
};
/** 删除按钮操作 */
const handleDelete = async (row?: PayRecordVO) => {
const _ids = row?.id || ids.value;

View File

@@ -24,40 +24,38 @@
</transition>
<el-card class="flex-1" shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="payRecordListList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="payRecordListList">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="仪表名称" align="center" prop="deviceType">
<template #default="scope">
<dict-tag :options="com_waterelectricity_topup_type" :value="scope.row.deviceType"></dict-tag>
</template>
</el-table-column>
<el-table-column label="设备编号" align="center" prop="deviceCode" />
<el-table-column label="房间号" align="center" prop="houseName" />
<el-table-column label="仪表名称" align="center" prop="payType" />
<el-table-column label="收费项目" align="center" prop="payType">
<el-table-column label="收费项目" align="center" prop="payType" width="160">
<template #default="scope">
<dict-tag :options="com_payrecord_pay_type" :value="scope.row.payType"></dict-tag>
</template>
</el-table-column>
<el-table-column label="抄表月份" align="center" prop="recordMonth" width="180">
<el-table-column label="抄表月份" align="center" prop="recordMonth" width="100">
<template #default="scope">
<span>{{ parseTime(scope.row.recordMonth, '{y}-{m}') }}</span>
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
<el-table-column label="开始时间" align="center" prop="startTime" width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="180">
<el-table-column label="结束时间" align="center" prop="endTime" width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="本期用度" align="center" prop="currentCost" />
<el-table-column label="费用合计(元)" align="center" prop="totalCost" />
<el-table-column label="设备编号" align="center" prop="deviceCode" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<el-table-column label="本期用度" align="center" prop="currentCost" width="160" />
<el-table-column label="费用合计(元)" align="center" prop="totalCost" width="120" />
<el-table-column label="操作" align="center" width="160" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)">详情</el-button>
</template>
@@ -148,7 +146,9 @@ import { listPayRecordList, getPayRecordList, delPayRecordList, addPayRecordList
import { PayRecordListVO, PayRecordListQuery, PayRecordListForm } from '@/api/system/SdbPayRecordMain/type';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_payrecord_pay_type } = toRefs<any>(proxy?.useDict('com_payrecord_pay_type'));
const { com_payrecord_pay_type, com_waterelectricity_topup_type } = toRefs<any>(
proxy?.useDict('com_payrecord_pay_type', 'com_waterelectricity_topup_type')
);
const payRecordListList = ref<PayRecordListVO[]>([]);
const buttonLoading = ref(false);
@@ -159,6 +159,7 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const router = useRouter();
const route = useRoute();
const queryFormRef = ref<ElFormInstance>();
const payRecordListFormRef = ref<ElFormInstance>();
@@ -218,10 +219,18 @@ const deviceCodeList = [
];
const { queryParams, form } = toRefs(data);
const queryid = ref('');
if (route.query.id) {
queryid.value = route.query.id as string;
}
/** 查询抄表周期明细列表 */
const getList = async () => {
loading.value = true;
const res = await listPayRecordList(queryParams.value);
const res = await listPayRecordList({
...queryParams.value,
recordId: queryid.value ?? undefined
});
payRecordListList.value = res.rows;
total.value = res.total;
loading.value = false;

View File

@@ -10,7 +10,13 @@
</el-form-item>
<el-form-item label="收费项目" prop="chargeItemId">
<el-select 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
:disabled="item.status === '1'"
v-for="(item, index) in chargeitemlist"
:key="index"
:value="item.id"
:label="item.itemName"
></el-option>
</el-select>
</el-form-item>
<!-- 水费电费 收费范围为 房屋 -->
@@ -27,6 +33,7 @@
</el-radio-group>
<div @click="handleClearCheckout" v-if="form.chargeScope === '1'" class="ml-10px cursor-pointer color-red">清空</div>
</el-form-item>
<!-- 房屋选择 -->
<el-form-item v-if="form.chargeScope === '1' && typeNamelist.includes(typeName)">
<div class="residentBox">
<ul class="checkoutSideUsesbox">
@@ -52,6 +59,7 @@
</ul>
</div>
</el-form-item>
<!-- 业主选择 -->
<el-form-item v-if="form.chargeScope === '1' && !typeNamelist.includes(typeName)">
<div class="residentBox">
<ul class="checkoutSideUsesbox">
@@ -113,7 +121,7 @@ import PageHeader from '@/components/Pageheader/index.vue';
import { listChargeItem } from '@/api/system/SdbChargeItem';
import { getBill, updateBill, addBill, BillHouse, listBillHouse, queryResidentList_holder } from '@/api/system/SdbBill';
import type { DatePickerType } from 'element-plus';
import { formatDate, formatDate2 } from '@/utils';
import { formatDate2 } from '@/utils';
import { Delete } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;

View File

@@ -48,9 +48,13 @@
<el-table-column label="账单名称" align="center" prop="billName" />
<el-table-column label="收费范围" align="center" prop="chargeScope" width="100">
<template #default="scope">
<!-- TODO 收费类型来判断收费范围 -->
<template v-if="ShowHouselist.includes(scope.row.chargeTypeId)">
<dict-tag :options="com_bill_charge_scope_house" :value="scope.row.chargeScope"></dict-tag>
</template>
<template v-else>
<dict-tag :options="com_questionnaire_scope" :value="scope.row.chargeScope"></dict-tag>
</template>
</template>
</el-table-column>
<el-table-column label="收费项目" align="center" prop="chargeItemName" />
<!-- <el-table-column label="收费周期" align="center" prop="chargePeriod" width="80">
@@ -68,7 +72,7 @@
<template #default="scope">
<div class="flex justify-center">
<span>{{ scope.row.fixedPrice }}</span> /
<dict-tag :options="com_charge_item_unit" value="0"></dict-tag>
<dict-tag :options="com_charge_item_unit" :value="scope.row.unit"></dict-tag>
</div>
</template>
</el-table-column>
@@ -97,7 +101,7 @@ import { checkPermi } from '@/utils/permission';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_bill_search_filter } = toRefs<any>(proxy?.useDict('com_bill_search_filter'));
const { com_questionnaire_scope } = toRefs<any>(proxy?.useDict('com_questionnaire_scope'));
const { com_bill_charge_period } = toRefs<any>(proxy?.useDict('com_bill_charge_period'));
// const { com_bill_charge_period } = toRefs<any>(proxy?.useDict('com_bill_charge_period'));
const { com_bill_charge_scope_house } = toRefs<any>(proxy?.useDict('com_bill_charge_scope_house'));
const { com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_charge_item_unit'));
@@ -118,6 +122,8 @@ const dialog = reactive<DialogOption>({
title: ''
});
const ShowHouselist = ['2049026484272791553'];
const initFormData: BillForm = {
id: undefined,
billCode: undefined,
@@ -135,9 +141,10 @@ const data = reactive<PageData<BillForm, BillQuery>>({
queryParams: {
pageNum: 1,
pageSize: 20,
billCode: '',
type: '',
value: ''
chargeItemId: '',
billCode: ''
// type: '',
// value: ''
}
});

View File

@@ -22,17 +22,17 @@
<div v-else-if="scope.row.chargeTypeName === '电费' || scope.row.chargeTypeName === '水费'">{{ scope.row.houseName }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="收费项目">
<el-table-column align="center" label="收费项目" width="250">
<template #default="scope">
<div>{{ scope.row.chargeItemName }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="收费周期">
<el-table-column align="center" label="收费周期" width="300">
<template #default="scope">
<div>{{ scope.row.startDate }} -- {{ scope.row.endDate }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="单价">
<el-table-column align="center" label="单价" width="150">
<template #default="scope">
<div>{{ scope.row.fixedPrice }}</div>
</template>
@@ -42,13 +42,13 @@
<div>{{ scope.row.formula }}</div>
</template>
</el-table-column> -->
<el-table-column align="center" label="缴费状态">
<el-table-column align="center" label="缴费状态" width="120">
<template #default="scope">
<dict-tag v-if="!inglelist.includes(scope.row.chargeItemName)" :options="com_pay_status" :value="scope.row.payStatus"></dict-tag>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column align="center" label="应缴金额">
<el-table-column align="center" label="应缴金额" width="120">
<template #default="scope">
<div v-if="!inglelist.includes(scope.row.chargeItemName)">{{ scope.row.money ?? '--' }}</div>
<div v-else>--</div>
@@ -67,7 +67,7 @@
</div>
<div>
<span>账单名称 </span>
<span style="text-wrap: wrap; text-indent: 2rem">{{ from.billResidentId.join('、 ') }}</span>
<span style="text-wrap: wrap; text-indent: 2rem">{{ from.billResidentId.map((item) => item.chargeItemName).join('、 ') }}</span>
</div>
<div>
<span>应收合计{{ from.allCost }}</span>
@@ -157,11 +157,8 @@ const getList = () => {
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: billlistType[]) => {
// TODO: 获取选中的id
ids.value = [];
ids.value = selection;
console.log(ids.value);
console.log(selection);
single.value = selection.length != 1;
multiple.value = !selection.length;
@@ -189,15 +186,20 @@ const from = ref({
});
const openDiage = () => {
if (!ids.value.length) {
ElMessage.warning('请至少选择一条账单进行收款');
return;
}
from.value = {
allCost: 0,
payStatus: '0',
billResidentId: ['202505电费', '202505水费'],
billResidentId: [],
payType: '0',
remark: ''
};
// 计算金额
console.log(ids.value);
ids.value.forEach((item) => {
from.value.billResidentId.push(item);
from.value.allCost += Number(item.money);
});
dialog.visible = true;

View File

@@ -132,6 +132,7 @@ const queryParams = ref({
pageSize: 10
});
const tabledata = ref([]);
// 支付状态 0未缴纳 1已缴纳 2缴纳失败
const getList = () => {
billQuery({
residentId: props.query.residentId,
@@ -144,7 +145,6 @@ const getList = () => {
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: billlistType[]) => {
// TODO: 获取选中的id
ids.value = selection.map((item) => item.residentId);
single.value = selection.length != 1;
multiple.value = !selection.length;

View File

@@ -13,7 +13,7 @@
</el-select>
</div>
<div class="searchBox">
<el-input v-model="searchValue" @keyup.enter="getlist">
<el-input clearable v-model="searchValue" @keyup.enter="getlist">
<template #suffix>
<el-icon @click="getlist" class="cursor-pointer">
<Search></Search>
@@ -21,16 +21,19 @@
</template>
</el-input>
<div class="listbox" v-if="list.length">
<div class="listbox" v-if="onceSearch || Searching">
<div class="list_item">
<div class="title">
<DictTag :options="com_cashier_type" :value="searchType"></DictTag>
<DictTag :options="com_cashier_type" :value="LazysearchType"></DictTag>
</div>
<div class="list_body">
<div class="list_body" v-loading="Searching" v-if="list.length > 0 || Searching">
<div class="list_value" @click="searchclick(item)" v-for="(item, index) in list" :key="index">
{{ item.houseName }}-{{ item.residentName }}-{{ item.phone }}
</div>
</div>
<div v-else class="flex h-full items-center justify-center">
<span class="noData">暂无数据</span>
</div>
</div>
</div>
</div>
@@ -58,25 +61,30 @@
</template>
<script setup lang="ts">
// TODO 收银台
// TODO 历史缴费
// TODO 添加临时收费
// TODO 预存
// TODO 缴费
import { Search } from '@element-plus/icons-vue';
import { Component, DefineComponent, ref } from 'vue';
import { Component, ref } from 'vue';
import cashier from './cashier.vue';
import history from './history.vue';
import { debounce } from '@/utils';
import { cashQuery } from '@/api/system/SdbCashier';
/** 当前状态 搜索后 搜索前 */
const isSearch = ref(true);
/** 为了loading 搜索结果 添加防抖 */
const Searching = ref(false);
const onceSearch = ref(false);
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_cashier_type } = toRefs<any>(proxy?.useDict('com_cashier_type'));
console.log(com_cashier_type);
/** 房屋 住户 车辆 车位 */
const com_cashier_type_list = ref(['houseName', 'residentName']);
const searchform = ref();
const searchValue = ref('');
const searchType = ref('residentName');
const searchType = ref('houseName');
const LazysearchType = ref('');
function handleChangeSearchType() {
list.value = [];
searchValue.value = '';
@@ -126,12 +134,45 @@ function handleChecout(index: TabValue) {
}
// ! ===================================================================================
// 用于追踪最新请求的标识
let latestRequestId = 0;
function getlist() {
console.log('search2');
// 生成当前请求的唯一ID
const currentRequestId = ++latestRequestId;
const currenttime = Date.now();
Searching.value = true;
searchform.value = {};
searchform.value[searchType.value] = searchValue.value;
cashQuery(searchform.value).then((res) => {
list.value = res.data;
let lastdata = [];
cashQuery(searchform.value)
.then((res) => {
// 检查是否是最新的请求,防止竞态条件
if (currentRequestId === latestRequestId) {
lastdata = res.data;
}
})
.finally(() => {
if (currentRequestId === latestRequestId) {
LazysearchType.value = searchType.value;
onceSearch.value = true;
// 计算已过去的时间
const elapsedTime = Date.now() - currenttime;
const minLoadingTime = 300;
if (elapsedTime < minLoadingTime) {
setTimeout(() => {
// 再次检查,防止在等待期间发起了新请求
if (currentRequestId === latestRequestId) {
list.value = lastdata;
Searching.value = false;
}
}, minLoadingTime - elapsedTime);
} else {
list.value = lastdata;
Searching.value = false;
}
}
});
}
// ! ===================================================================================
@@ -142,6 +183,9 @@ const handleBack = () => {
</script>
<style scoped lang="scss">
.noData {
color: #999999;
}
.tabsbox {
height: 60px;
background-color: #fff;
@@ -203,11 +247,15 @@ const handleBack = () => {
}
.listbox {
border: 1px solid #ccc;
width: 100%;
padding: 15px;
min-height: 200px;
font-size: 16px;
margin-top: -2px;
box-sizing: border-box;
border-radius: 0px 0px 5px 5px;
background-color: rgba(255, 255, 255, 1);
color: rgba(16, 16, 16, 1);
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.1);
.list_item {
padding-bottom: 30px;

View File

@@ -66,7 +66,7 @@
<el-table-column label="设备编码" width="250" align="center" prop="deviceCode" />
<el-table-column label="设备状态" width="100" align="center" prop="status">
<template #default="scope">
<div class="readRecord" v-if="scope.row.status">
<div class="readRecord" v-if="scope.row.status !== null">
<DictTag :options="com_waterdevice_on_status" :value="scope.row.status"></DictTag>
</div>
<span v-else>---</span>
@@ -121,16 +121,15 @@
></el-switch>
</template>
</el-table-column> -->
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<el-table-column min-width="120" label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-row>
<el-col :span="24" class="flex flex-items-center actions">
<div class="actions">
<el-button link type="primary" @click="handleRecode(scope.row)" v-hasPermi="['equipment:view:electricitydevice']">充值记录</el-button>
<el-button link type="primary" @click="handleQrCode(scope.row)" v-hasPermi="['equipment:addqrcode:electricitydevice']"
>生成条码</el-button
>
<el-button link type="primary" @click="handleOpenDialog('usedWater', scope.row)" v-hasPermi="['equipment:useWater:electricitydevice']"
></el-button
></el-button
>
<el-button
link
@@ -166,8 +165,7 @@
</el-dropdown-menu>
</template>
</el-dropdown> -->
</el-col>
</el-row>
</div>
</template>
</el-table-column>
</el-table>
@@ -180,19 +178,21 @@
<el-form-item label="设备编号" prop="deviceCode"> {{ UsedWaterform.deviceCode }} </el-form-item>
<el-form-item label="开始时间" prop="startTime">
<el-date-picker
format="YYYY-MM-DD HH:MM:ss"
value-format="YYYY-MM-DD HH:MM:ss"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="UsedWaterform.startTime"
type="datetime"
@update:model-value="handleStarTime"
placeholder="开始时间"
/>
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker
format="YYYY-MM-DD HH:MM:ss"
value-format="YYYY-MM-DD HH:MM:ss"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="UsedWaterform.endTime"
type="datetime"
@update:model-value="handleEndTime"
placeholder="结束时间"
/>
</el-form-item>
@@ -326,6 +326,26 @@ const rules = {
startTime: [{ required: true, message: '请选择开始时间', trigger: 'blur' }],
endTime: [{ required: true, message: '请选择结束时间', trigger: 'blur' }]
};
function handleStarTime(val: string) {
if (!val) return;
const current = new Date(val).getTime();
if (UsedWaterform.value.endTime && UsedWaterform.value.startTime) {
if (current > new Date(UsedWaterform.value.endTime).getTime()) {
UsedWaterform.value.endTime = '';
ElMessage.warning('开始时间不能大于结束时间');
}
}
}
function handleEndTime(val: string) {
if (!val) return;
const current = new Date(val).getTime();
if (UsedWaterform.value.startTime && UsedWaterform.value.endTime) {
if (current < new Date(UsedWaterform.value.startTime).getTime()) {
UsedWaterform.value.startTime = '';
ElMessage.warning('结束时间不能小于开始时间');
}
}
}
// ! =======================================================================================
// ? 上传固件 ==============================================================================================
@@ -464,7 +484,7 @@ function getTree() {
}
/** 电表 启停*/
// TODO 修改电表 启停
// TODO 修改电表 启停
const handleChangeWater = (val: string, row: ElectricityDeviceVO) => {};
/** 提交按钮 */
const submitForm = () => {
@@ -579,9 +599,18 @@ const download = (url: string) => {
<style lang="scss" scoped>
.actions {
:deep(.el-button + .el-button) {
margin-left: 0;
}
:deep(.el-button > span) {
font-weight: 650;
}
@media (max-width: 1500px) {
display: flex;
flex-direction: column;
align-items: center;
}
}
:deep(.waterDevice_online) {
color: #3dd4a7;

View File

@@ -4,7 +4,7 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" label-width="100px" @submit.prevent="handleQuery" :model="queryParams" :inline="true">
<el-form ref="queryFormRef" label-width="70px" @submit.prevent="handleQuery" :model="queryParams" :inline="true">
<!-- <el-form-item label="设备状态" prop="eleStatus">
<el-select v-model="queryParams.eleStatus">
<el-option v-for="(item, index) in com_waterdevice_on_status" :key="index" :label="item.label" :value="item.value"></el-option>
@@ -15,8 +15,11 @@
<el-option v-for="(item, index) in com_water_readrecord_status" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item> -->
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="queryParams.deviceCode" placeholder="请输入" clearable />
</el-form-item>
<el-form-item label="房屋" prop="houseName">
<el-input v-model="queryParams.houseName" placeholder="请输入" clearable />
<el-input clearable v-model="queryParams.houseName" placeholder="请输入" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
@@ -38,6 +41,9 @@
<el-dropdown-item @click="handleMarkqrcode" v-if="checkPermi(['equipment:addqrcode:waterdevice'])">
<span>批量生成条形码</span>
</el-dropdown-item>
<el-dropdown-item @click="handleMarkQrcodeZip">
<span>批量下载条形码</span>
</el-dropdown-item>
<!-- <el-dropdown-item v-hasPermi="['equipment:addqrcode:electricitydevice']">
<div @click="handleMarkqrcode">批量更新固件</div>
</el-dropdown-item> -->
@@ -51,13 +57,14 @@
<el-table-column label="设备编码" width="250" align="center" prop="deviceCode" />
<el-table-column label="设备状态" width="100" align="center" prop="eleStatus">
<template #default="scope">
<dict-tag v-if="scope.row.waterRecord" :options="com_waterdevice_on_status" :value="scope.row.waterRecord.eleStatus"></dict-tag>
<!-- 1 离线 0在线 -->
<dict-tag v-if="scope.row.waterRecord" :options="com_waterdevice_on_status" :value="Number(scope.row.status)"></dict-tag>
<span v-else>---</span>
</template>
</el-table-column>
<el-table-column label="分合状态" width="100" align="center">
<template #default="scope">
<dict-tag v-if="scope.row.status" :options="com_water_readrecord_status" :value="scope.row.waterRecord.status"></dict-tag>
<dict-tag v-if="scope.row.status !== null" :options="com_water_readrecord_status" :value="scope.row.waterRecord.status"></dict-tag>
<span v-else>---</span>
</template>
</el-table-column>
@@ -102,10 +109,9 @@
></el-switch>
</template>
</el-table-column> -->
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<el-table-column label="操作" min-width="120" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-row>
<el-col :span="24" class="flex flex-items-center actions">
<div class="actions">
<el-button link type="primary" @click="handleRecode(scope.row)" v-hasPermi="['equipment:view:waterdevice']">充值记录</el-button>
<el-button
link
@@ -121,6 +127,8 @@
<el-button link type="primary" @click="handleOpenDialog('Upload', scope.row)" v-hasPermi="['equipment:updatehardware:waterdevice']"
>更新固件</el-button
>
<el-button link type="primary" @click="handleUpdate(scope.row.deviceCode)">最新抄表记录</el-button>
<el-button link type="primary" @click="handleOpenDialog('UpdateHouse', scope.row)" v-hasPermi="['equipment:edithouse:waterdevice']"
>绑定房屋</el-button
>
@@ -144,13 +152,19 @@
</el-dropdown-menu>
</template>
</el-dropdown> -->
</el-col>
</el-row>
</div>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<pagination
:pageSizes="[10, 30, 50, 100]"
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-card>
<!-- 添加或修改水 设备对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
@@ -163,6 +177,7 @@
value-format="YYYY-MM-DD HH:mm:ss"
v-model="UsedWaterform.startTime"
type="datetime"
@update:model-value="handleStarTime"
placeholder="开始时间"
/>
</el-form-item>
@@ -172,6 +187,7 @@
value-format="YYYY-MM-DD HH:mm:ss"
v-model="UsedWaterform.endTime"
type="datetime"
@update:model-value="handleEndTime"
placeholder="结束时间"
/>
</el-form-item>
@@ -251,6 +267,19 @@
<div class="useditem">用水量</div>
<div class="useditem value">{{ UsedWaterInfo.count }}</div>
</div>
<el-form v-if="dialogType === 'deviceLastRecord'" ref="deviceLastRecordformRef" :model="EditHouseform" label-width="100px">
<el-form-item label="设备编号" prop="deviceCode"> {{ deviceLastRecordform.deviceCode }} </el-form-item>
<el-form-item label="读表时间" prop="readTime"> {{ deviceLastRecordform.readTime }} </el-form-item>
<el-form-item label="阀门状态" prop="status">
<DictTag :options="com_water_readrecord_status" :value="deviceLastRecordform.status"></DictTag>
</el-form-item>
<el-form-item label="余额" prop="balance"> {{ deviceLastRecordform.balance }} </el-form-item>
<el-form-item label="用水量" prop="totalWater"> {{ deviceLastRecordform.totalWater }} </el-form-item>
<el-form-item>
<el-button @click="unSubmit">关闭</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
@@ -262,18 +291,17 @@ import {
getWaterDeviceTimeRangeUsedWater,
WaterDeviceMakerQrcode,
updateWaterDevice,
WaterDeviceUpdateBin
WaterDeviceUpdateBin,
latestRecord
} from '@/api/system/SdbWaterDevice/index';
import { WaterDeviceVO, WaterDeviceQuery, WaterDeviceForm } from '@/api/system/SdbWaterDevice/type';
import { WaterDeviceVO } from '@/api/system/SdbWaterDevice/type';
import { useHouseStore } from '@/store/modules/house';
import { useUserStore } from '@/store/modules/user';
import { getHousePathById } from '@/utils/house';
import { Upload } from '@element-plus/icons-vue';
import { CascaderValue, tourEmits, UploadProps, UploadRawFile } from 'element-plus';
import { formatDate } from '@/utils/time';
import { CascaderValue, UploadProps, UploadRawFile } from 'element-plus';
import { checkPermi } from '@/utils/permission';
import { exportImagesToZip } from '@/utils/exportZip';
const huhao = 3096371295;
const uploadAUrl = import.meta.env.VITE_APP_BASE_API + '/waterDevice/uploadA';
const uploadBUrl = import.meta.env.VITE_APP_BASE_API + '/waterDevice/uploadB';
@@ -285,7 +313,7 @@ const waterDeviceList = ref<WaterDeviceVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<{ id: string; deviceCode: string }[]>([]);
const ids = ref<WaterDeviceVO[]>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
@@ -303,19 +331,21 @@ const data = reactive({
pageSize: 10,
eleStatus: '',
houseName: '',
status: ''
status: '',
deviceCode: ''
}
});
const { queryParams } = toRefs(data);
/** 查询水 设备列表 */
const getList = async () => {
const getList = async (callback?: (vals: WaterDeviceVO[]) => void) => {
loading.value = true;
const res = await listWaterDevice(queryParams.value);
waterDeviceList.value = res.rows;
total.value = res.total;
loading.value = false;
callback && callback(waterDeviceList.value);
};
// ! 用水量 =============================================================================
const usedWaterInfo = ref(true);
@@ -325,6 +355,27 @@ const UsedWaterform = ref({
startTime: '',
endTime: ''
});
function handleStarTime(val: string) {
if (!val) return;
const current = new Date(val).getTime();
if (UsedWaterform.value.endTime && UsedWaterform.value.startTime) {
if (current > new Date(UsedWaterform.value.endTime).getTime()) {
UsedWaterform.value.endTime = '';
ElMessage.warning('开始时间不能大于结束时间');
}
}
}
function handleEndTime(val: string) {
if (!val) return;
const current = new Date(val).getTime();
if (UsedWaterform.value.startTime && UsedWaterform.value.endTime) {
if (current < new Date(UsedWaterform.value.startTime).getTime()) {
UsedWaterform.value.startTime = '';
ElMessage.warning('结束时间不能小于开始时间');
}
}
}
const UsedWaterInfo = ref({
count: '',
startTime: '',
@@ -334,6 +385,7 @@ const rules = {
startTime: [{ required: true, message: '请选择开始时间', trigger: 'blur' }],
endTime: [{ required: true, message: '请选择结束时间', trigger: 'blur' }]
};
// ! =======================================================================================
// ? 上传固件 ==============================================================================================
@@ -395,7 +447,19 @@ const EditHouseform = ref({
houseId: ''
});
// * 编辑房屋 ==============================================================================================
const deviceLastRecordformRef = ref(null);
const deviceLastRecordform = ref({
id: '',
deviceCode: '',
balance: '',
eleStatus: '',
readTime: '',
status: '',
totalWater: '',
type: ''
});
const imageRefs = ref([]);
/** 批量生成 条形码 */
function handleMarkqrcode() {
if (ids.value.length > 0) {
@@ -407,13 +471,33 @@ function handleMarkqrcode() {
ElMessage.warning('请选择设备');
}
}
function handleMarkQrcodeZip() {
if (ids.value.length > 0) {
const selectedIds = new Set(ids.value.map((item) => item.id));
const matchedList = waterDeviceList.value.filter((item) => {
return selectedIds.has(item.id) && item.url;
});
produceQRzip(matchedList);
} else {
ElMessage.warning('请选择设备');
}
}
function produceQRzip(vals: WaterDeviceVO[]) {
const arr = vals.map((item) => {
return {
name: item.deviceCode,
url: item.url
};
});
exportImagesToZip(arr);
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: WaterDeviceVO[]) => {
ids.value = selection.map((item) => {
return {
id: item.id,
deviceCode: item.deviceCode
};
return item;
});
single.value = selection.length != 1;
multiple.value = !selection.length;
@@ -430,10 +514,13 @@ const handleQrCode = (row: WaterDeviceVO) => {
id: row.id,
deviceCode: row.deviceCode
}
]).then(() => {
])
.then(() => {
ElMessage.success('生成成功');
loadingQRCode.value = loadingQRCode.value.filter((item) => item !== row.id);
getList();
})
.finally(() => {
loadingQRCode.value = loadingQRCode.value.filter((item) => item !== row.id);
});
}, 1000);
};
@@ -446,26 +533,31 @@ function handleChange(val: CascaderValue) {
EditHouseform.value.houseId = val[2];
}
// * house
const dialogType = ref<'usedWater' | 'Upload' | 'UpdateHouse'>('usedWater');
const dialogType = ref<'usedWater' | 'Upload' | 'UpdateHouse' | 'deviceLastRecord'>('usedWater');
/** 用水量 更新固件 编辑房屋*/
const handleOpenDialog = (type: 'usedWater' | 'Upload' | 'UpdateHouse', row: WaterDeviceVO) => {
const handleOpenDialog = (type: 'usedWater' | 'Upload' | 'UpdateHouse' | 'deviceLastRecord', row?: WaterDeviceVO) => {
reset();
if (type === 'usedWater') {
UsedWaterform.value.deviceCode = row.deviceCode;
UsedWaterform.value.startTime = '';
UsedWaterform.value.endTime = '';
usedWaterInfo.value = true;
dialog.title = '用水量查询';
} else if (type === 'Upload') {
UploadFileform.value.deviceCodes = [row.deviceCode];
dialog.title = '固件上传';
} else if (type === 'UpdateHouse') {
EditHouseform.value.id = row.id;
EditHouseform.value.deviceCode = row.deviceCode;
EditHouseform.value.houseId = '';
dialog.title = '编辑户号';
getTree();
} else if (type === 'deviceLastRecord') {
// 处理设备最后记录的逻辑
dialog.title = '设备最新抄表记录';
}
dialog.visible = true;
dialogType.value = type;
dialog.title = type === 'usedWater' ? '用水量查询' : type === 'UpdateHouse' ? '编辑户号' : '更新固件';
};
function getTree() {
@@ -479,6 +571,26 @@ function getTree() {
/** 水表 启停*/
const handleChangeWater = (val: string, row: WaterDeviceVO) => {};
function handleUpdate(id: string) {
latestRecord(id).then((res) => {
console.log('设备最新抄表记录', res);
deviceLastRecordform.value = {
id: '',
deviceCode: '',
balance: '',
eleStatus: '',
readTime: '',
status: '',
totalWater: '',
type: ''
};
deviceLastRecordform.value = {
...res.data
};
handleOpenDialog('deviceLastRecord');
});
}
/** 提交按钮 */
const submitForm = () => {
if (dialogType.value === 'usedWater') {

View File

@@ -25,8 +25,9 @@
<el-row>
<el-col :span="11">
<el-form-item label="车牌号" prop="carNum">
<el-input v-model.trim="form.carNum" placeholder="请输入车牌号" />
<el-form-item label="车牌号" prop="carNum" :validate-event="false">
<!-- <el-input v-model.trim="form.carNum" placeholder="请输入车牌号" /> -->
<LicensePlateInput ref="carNumInputRef" required v-model="form.carNum" @change="() => handleValidate()" />
</el-form-item>
</el-col>
<el-col :span="2"></el-col>
@@ -79,7 +80,7 @@
<el-col :span="24">
<el-form-item label="持有人">
<!-- 公共组件 持有人 -->
<Holder :userid="form.residentId" @change="handleChange"></Holder>
<Holder :userid="form.residentId ?? ''" @change="handleChange"></Holder>
</el-form-item>
</el-col>
</el-row>
@@ -102,6 +103,7 @@
<script setup lang="ts">
import { ref } from 'vue';
import LicensePlateInput from '@/components/LicensePlateInput/index.vue';
import Holder from '@/components/Holder/index.vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { listArea } from '@/api/system/carArea';
@@ -109,7 +111,7 @@ import { listParking } from '@/api/system/parking';
import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus';
import { addCar, getCar, updateCar, uploadCar } from '@/api/system/cars';
import { useUserStore } from '@/store/modules/user';
import { validator } from '@/utils/Reg';
import { LicensePlateValidator, validator } from '@/utils/Reg';
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/car/uploadImage';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -117,10 +119,11 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
const route = useRoute();
const formRef = ref();
const carNumInputRef = ref();
const form = ref({
id: null,
areaId: null, // 区域id
residentId: null, // 车位id
residentId: '', // 车位id
parkingId: null, // 持有人id
carNum: '', //车牌号
carBrand: '', //车辆品牌
@@ -134,15 +137,31 @@ const rules = ref({
areaId: [{ required: true, message: '请选择区域', trigger: 'blur' }],
residentId: [{ required: true, message: '请选择', trigger: 'blur' }],
carNum: [
{ required: true, message: '请输入车牌号', trigger: 'blur' },
{
validator: (_, value) => validator(value, 'PlateNumber'),
message: '请输入车牌号',
trigger: 'blur'
required: true,
message: '请输入车牌号',
trigger: ['blur']
},
{
validator: (rule: any, value: string, callback: any) => {
if (!value) {
callback();
return;
}
if (!LicensePlateValidator.isValidPlate(value)) {
callback(new Error('请输入正确的车牌号格式'));
return;
}
callback();
},
trigger: 'blur' // ✅ 只在失焦时验证
}
],
carBrand: [{ required: true, message: '请输入车辆品牌', trigger: 'blur' }]
});
function handleValidate() {
formRef.value.validateField('carNum');
}
function headerToken() {
const useStore = useUserStore();
return {
@@ -204,6 +223,12 @@ function changeArealist(val: string) {
// !== 提交 =============================================================================
// 提交
const submitForm = () => {
const plateResult = carNumInputRef.value.validate();
if (!plateResult.valid) {
// 自动聚焦到错误字段
carNumInputRef.value.focus();
return;
}
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (userid.value.trim() !== '') {

View File

@@ -60,12 +60,8 @@
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['car:car:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['car:car:remove']"></el-button>
</el-tooltip>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['car:car:edit']">修改</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['car:car:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -138,8 +134,9 @@ const { queryParams, form, rules } = toRefs(data);
/** 持有人 */
const ResidentList = ref([]);
const getResidentName = (id: string | number) => {
const find = ResidentList.value.find((item) => item.id == id);
const getResidentName = (id: string) => {
const find = ResidentList.value.find((item) => item.id === id);
if (!find) return '--';
return find.name;
};

View File

@@ -136,7 +136,7 @@ const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dateRange = ref<[string, string]>(['', '']);
const queryFormRef = ref<ElFormInstance>();
const configFormRef = ref<ElFormInstance>();

View File

@@ -215,12 +215,14 @@
</el-form-item>
<el-form-item prop="dictType">
<el-input v-model="typeForm.dictType" placeholder="请输入字典类型" maxlength="100" />
<span slot="label">
<template v-slot:label>
<span>
<el-tooltip content="数据存储中的Key值sys_user_sex" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
字典类型
</span>
</template>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="typeForm.remark" type="textarea" placeholder="请输入内容"></el-input>
@@ -292,7 +294,7 @@ const typeIds = ref<Array<number | string>>([]);
const typeSingle = ref(true);
const typeMultiple = ref(true);
const typeTotal = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dateRange = ref<[string, string]>(['', '']);
const typeFormRef = ref<ElFormInstance>();
const typeQueryFormRef = ref<ElFormInstance>();

View File

@@ -73,7 +73,7 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getlist" />
</div>
</div>
</div>
@@ -93,7 +93,7 @@ const router = useRouter();
const route = useRoute();
const form = ref({
id: null,
categoryId: '', // 分类id、
categoryName: '', // 分类id、
deviceName: '', // 设备名称
deviceCode: '', // 编号
deviceModel: '', // 型号
@@ -121,7 +121,7 @@ function init() {
getDevice(userid.value).then(async (res) => {
form.value = {
id: res.data.id,
categoryId: res.data.categoryId,
categoryName: res.data.categoryName,
deviceName: res.data.deviceName,
deviceCode: res.data.deviceCode,
deviceModel: res.data.deviceModel,

View File

@@ -75,7 +75,7 @@
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { useHouseStore } from '@/store/modules/house';
import { addHouseAPI, EditHouseApi, getHouseApi, getHouselistAPI } from '@/api/system/house';
import { addHouseAPI, EditHouseApi, getBuildingOnly, getHouseApi, getHouselistAPI } from '@/api/system/house';
import { addHouseType } from '@/api/system/house/type';
const router = useRouter();
@@ -128,7 +128,7 @@ if (route.query.id) {
// 选择楼栋
function handlechangeBuild(id: string) {
getHouselistAPI(id).then((res) => {
getBuildingOnly(id).then((res) => {
units.value = Array.from({ length: res.data.unitCount }).map((_, index) => {
return {
no: index + 1,

View File

@@ -3,7 +3,7 @@
<div class="buildingheader flex align-center justify-between">
<div>楼栋</div>
<div>
<span class="pointer" @click="handleAddBuilding" v-hasPermi="['building:building:add']">+增加楼栋</span>
<span class="pointer" @click="handleAddBuilding" v-hasPermi="['system:building:add']">+增加楼栋</span>
</div>
</div>
<ul>
@@ -16,8 +16,8 @@
>
<div class="buildingname" @click="switchBuilding(item.id ?? null)">{{ item.buildingName ?? '' }}</div>
<div class="buildactions flex align-center justify-around">
<el-icon class="pointer hover-color-blue" @click="editBuilding(item.id)" v-hasPermi="['building:building:edit']"><Setting /></el-icon>
<el-icon class="pointer hover-color-red" @click="deleteBuilding(item.id)" v-hasPermi="['building:building:remove']"><Delete /></el-icon>
<el-icon class="pointer hover-color-blue" @click="editBuilding(item.id)" v-hasPermi="['system:building:edit']"><Setting /></el-icon>
<el-icon class="pointer hover-color-red" @click="deleteBuilding(item.id)" v-hasPermi="['system:building:remove']"><Delete /></el-icon>
</div>
</li>
</ul>

View File

@@ -1,18 +1,41 @@
<template>
<div class="UnitBox">
<div class="Unit_top flex align-center justify-between">
<!-- <div>
<span class="text-size-16px text-color-black text-widget-bold mr-30px">
<strong>{{ houseStore.currentBuildingInfo?.buildingName }}</strong>
</span>
<span class="mr-15px" v-if="houseStore.currentBuildingInfo && houseStore.currentBuildingInfo?.buildToward"
>楼栋朝向:{{ houseStore.currentBuildingInfo?.buildToward ? `${houseStore.currentBuildingInfo?.buildToward}(m)` : '--' }}</span
>
<span class="mr-15px" v-if="houseStore.currentBuildingInfo && houseStore.currentBuildingInfo?.buildTall"
>楼高{{ houseStore.currentBuildingInfo?.buildTall ? `${houseStore.currentBuildingInfo?.buildTall}(m)` : '--' }}</span
>
<span class="mr-15px" v-if="houseStore.currentBuildingInfo && houseStore.currentBuildingInfo?.buildStructure"
>楼栋结构{{ houseStore.currentBuildingInfo?.buildStructure ? `${houseStore.currentBuildingInfo?.buildStructure}(m)` : '--' }}
</span>
</div> -->
<div>
<span class="text-size-16px text-color-black text-widget-bold mr-30px">
<strong>{{ houseStore.currentBuildingInfo?.buildingName }}</strong>
</span>
<span class="mr-30px">楼栋朝向:{{ houseStore.currentBuildingInfo?.buildToward }}</span>
<span class="mr-30px">{{ houseStore.currentBuildingInfo?.buildTall ? `${houseStore.currentBuildingInfo?.buildTall}(m)` : '' }}</span>
<span class="mr-30px">楼栋结构{{ houseStore.currentBuildingInfo?.buildStructure }} </span>
<span class="mr-15px"
>栋朝向:{{ houseStore.currentBuildingInfo?.buildToward ? `${houseStore.currentBuildingInfo?.buildToward}` : '--' }}</span
>
<span class="mr-15px">楼高{{ houseStore.currentBuildingInfo?.buildTall ? `${houseStore.currentBuildingInfo?.buildTall}(m)` : '--' }}</span>
<span class="mr-15px"
>楼栋结构{{ houseStore.currentBuildingInfo?.buildStructure ? `${houseStore.currentBuildingInfo?.buildStructure}` : '--' }}
</span>
</div>
<div class="rightActions">
<div class="rightActions flex flex-items-center">
<span class="mr-20px" @click="handleAddHouse" v-hasPermi="['system:house:add']">+增加房屋</span>
<!-- <span class="mr-20px">收费标准设置</span> -->
<span @click="deleteAllhouse" v-hasPermi="['system:house:remove']">删除</span>
<div class="searchbox flex flex-items-center">
<el-input v-model="housename" class="searchinput" placeholder="请输入房屋号"></el-input>
<el-button style="margin-left: 10px" type="primary" @click="handleSearchHouse">搜索</el-button>
<el-button @click="reset">重置</el-button>
</div>
</div>
</div>
<div class="UnitBody">
@@ -30,11 +53,23 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const houseStore = useHouseStore();
const router = useRouter();
const housename = ref('');
/** 添加房屋 */
function handleAddHouse() {
router.push({
path: '/house/addHouse'
});
}
function handleSearchHouse() {
if (housename.value && housename.value.trim()) {
houseStore.getHouseBybuildingNo(housename.value);
}
}
function reset() {
housename.value = '';
houseStore.getHouseBybuildingNo(housename.value);
}
async function deleteAllhouse() {
await proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗?');
@@ -68,9 +103,23 @@ $primary_color: #1978fe;
color: rgba(102, 102, 102, 1);
.rightActions {
color: $primary_color;
height: 35px;
span {
cursor: pointer;
}
.searchbox {
margin-left: 30px;
.searchinput {
width: 250px;
@media (max-width: 1200px) {
width: 50px;
}
@media (max-width: 1400px) {
width: 130px;
}
}
}
}
}
.UnitBody {

View File

@@ -102,6 +102,7 @@ const route = useRoute();
const router = useRouter();
const houseRentalid = ref(null);
const HouseInfo = ref({
'id': null,
'house': '',
'name': null,
'rentalStatus': null,

View File

@@ -35,6 +35,7 @@ const formRef = ref();
const form = ref({
id: null,
itemName: '', // 项目名称
status: '0', // 状态 0正常 1停用
inspectionStandard: null // 巡检标准
});
const rules = ref({
@@ -48,6 +49,7 @@ if (route.query.id) {
form.value = {
id: res.data.id,
itemName: res.data.itemName,
status: res.data.status,
inspectionStandard: res.data.inspectionStandard
};
});

View File

@@ -76,7 +76,6 @@
<el-table-column label="未开始" align="center" prop="pendingTask" />
</el-table>
<pagination
:pagerCount="pagerCount"
v-show="total > 0"
:total="total"
:layout="layout"
@@ -104,7 +103,6 @@ const total = ref(100);
const queryFormRef = ref<ElFormInstance>();
const pagerCount = document.body.clientWidth <= 1500 ? 1 : 7;
const layout = document.body.clientWidth <= 1500 ? 'total, sizes, prev, pager, next' : 'total, sizes, prev, pager, next,';
const disabledDate = (time: Date) => {
return time.getTime() > Date.now();
@@ -311,7 +309,8 @@ const getList = async () => {
list.value[4].value += item.outUncompletedTasks ?? 0;
});
if (!Number.isNaN(+list.value[2].value) && !Number.isNaN(+list.value[1].value)) {
list.value[3].value = Math.min((Number(list.value[2].value) / Number(list.value[1].value)) * 100, 100).toFixed(2) + '%';
const num = Number(list.value[2].value) / Number(list.value[1].value);
list.value[3].value = Math.min((Number.isNaN(num) ? 0 : num) * 100, 100).toFixed(2) + '%';
}
handleEcartsData(ecartslist.value.find((item) => item.key === activeEchartsindex.value));

View File

@@ -57,7 +57,13 @@
<el-table-column label="计划路线" align="center" prop="routeNames" />
<el-table-column label="状态" width="150" align="center" prop="status">
<template #default="scope">
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1"></el-switch>
<el-switch
@change="(val: string) => handleChangeStatus(scope.row.id, val)"
v-model="scope.row.status"
active-value="0"
inactive-value="1"
></el-switch>
<!-- 0关闭 1开启 -->
</template>
</el-table-column>
<el-table-column label="操作" width="110" align="center" fixed="right" class-name="small-padding fixed-width">
@@ -74,7 +80,7 @@
</template>
<script setup name="Plan" lang="ts">
import { listPlan, getPlan, delPlan, addPlan, updatePlan } from '@/api/system/InspectionPlan/index';
import { listPlan, getPlan, delPlan, addPlan, updatePlan, changePlanStatus } from '@/api/system/InspectionPlan/index';
import { PlanVO, PlanQuery, PlanForm } from '@/api/system/InspectionPlan/type';
import { checkPermi } from '@/utils/permission';
import { numToWeek } from '@/utils/time';
@@ -203,7 +209,13 @@ const handleUpdate = async (row?: PlanVO) => {
}
});
};
/** 状态修改 */
function handleChangeStatus(id: string, status: string) {
changePlanStatus(id, status).then(() => {
proxy?.$modal.msgSuccess('操作成功');
getList();
});
}
/** 提交按钮 */
const submitForm = () => {
planFormRef.value?.validate(async (valid: boolean) => {

View File

@@ -55,7 +55,13 @@
<el-table-column label="巡检点位置" align="center" prop="location" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<DictTag :options="com_inspection_point_status" :value="scope.row.status"></DictTag>
<el-switch
@change="(val: string) => handleChangeStatus(scope.row.id, val)"
v-model="scope.row.status"
active-value="0"
inactive-value="1"
></el-switch>
<!-- 0开启 1关闭 -->
</template>
</el-table-column>
<el-table-column label="添加时间" align="center" prop="createTime" />
@@ -73,8 +79,8 @@
</template>
<script setup name="Point" lang="ts">
import PageHeader from '@/components/Pageheader/index.vue';
import { listPoint, getPoint, delPoint, addPoint, updatePoint } from '@/api/system/InspectionPoint/index';
import Pageheader from '@/components/Pageheader/index.vue';
import { listPoint, getPoint, delPoint, addPoint, updatePoint, changePointStatus } from '@/api/system/InspectionPoint/index';
import { PointVO, PointQuery, PointForm } from '@/api/system/InspectionPoint/type';
import { checkPermi } from '@/utils/permission';
@@ -218,6 +224,12 @@ const handleDelete = async (row?: PointVO) => {
await getList();
};
function handleChangeStatus(id: string, status: string) {
changePointStatus(id, status).then(() => {
proxy?.$modal.msgSuccess('操作成功');
getList();
});
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(

View File

@@ -78,7 +78,6 @@ const form = ref<RecordVO>({
planName: '',
actualStartTime: '',
actualEndTime: '',
statusLabel: '',
createByName: '',
details: [],
createTime: '',
@@ -89,6 +88,7 @@ if (route.query.id) {
userid.value = route.query.id;
getRecord(userid.value).then((res) => {
form.value = {
...form.value,
...res.data
};
});
@@ -122,7 +122,7 @@ const tableData = [
// 推出
const cancelForm = () => {
router.push({
path: '/carArea/monthCard'
path: '/Inspection/Record'
});
};
</script>

View File

@@ -62,56 +62,14 @@
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:route:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:route:remove']"></el-button>
</el-tooltip>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['system:route:edit']">修改</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['system:route:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改巡检路线对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="routeFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="巡检路线名称" prop="routeName">
<el-input v-model="form.routeName" placeholder="请输入巡检路线名称" />
</el-form-item>
<el-form-item label="路线描述" prop="description">
<el-input v-model="form.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="巡检点" prop="totalPoints">
<el-input v-model="form.totalPoints" placeholder="请输入巡检点" />
</el-form-item>
<el-form-item label="预计巡检时长" prop="estimatedTime">
<el-input v-model="form.estimatedTime" placeholder="请输入预计巡检时长" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="起点经度" prop="startLng">
<el-input v-model="form.startLng" placeholder="请输入起点经度" />
</el-form-item>
<el-form-item label="起点纬度" prop="startLat">
<el-input v-model="form.startLat" placeholder="请输入起点纬度" />
</el-form-item>
<el-form-item label="终点经度" prop="endLng">
<el-input v-model="form.endLng" placeholder="请输入终点经度" />
</el-form-item>
<el-form-item label="终点纬度" prop="endLat">
<el-input v-model="form.endLat" placeholder="请输入终点纬度" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@@ -143,6 +101,7 @@ const dialog = reactive<DialogOption>({
const initFormData: RouteForm = {
id: undefined,
routeName: undefined,
pathPoints: undefined,
description: undefined,
totalPoints: undefined,
estimatedTime: undefined,
@@ -160,14 +119,14 @@ const data = reactive<PageData<RouteForm, RouteQuery>>({
pageSize: 10,
routeName: undefined,
description: undefined,
pathPoints: undefined,
totalPoints: undefined,
estimatedTime: undefined,
routeStatus: undefined,
startLng: undefined,
startLat: undefined,
endLng: undefined,
endLat: undefined,
params: {}
endLat: undefined
},
rules: {
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
@@ -258,7 +217,7 @@ const submitForm = () => {
/** 删除按钮操作 */
const handleDelete = async (row?: RouteVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除巡检路线编号为"' + _ids + '"的数据项').finally(() => (loading.value = false));
await proxy?.$modal.confirm('是否确认删除巡检路线?').finally(() => (loading.value = false));
await delRoute(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();

View File

@@ -37,7 +37,7 @@
<el-table v-loading="loading" border :data="routeList">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="巡检人员" align="center" prop="inspectorName" show-overflow-tooltip />
<el-table-column label="巡检人员" align="center" prop="roleName" show-overflow-tooltip />
<el-table-column label="计划巡检任务" align="center" prop="totalTasks" />
<el-table-column label="按时完成" align="center" prop="completedTasks">
<template #default="scope">
@@ -76,8 +76,6 @@ const data = reactive({
});
const checkdata = ref([]);
// TODO 巡检人员名称
const { queryParams } = toRefs(data);
/** 查询巡检路线列表 */
const getList = async () => {

View File

@@ -5,7 +5,7 @@
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" label-width="100px" @submit.prevent="handleQuery" :model="queryParams" :inline="true">
<el-form-item label="执行状态" prop="taskName">
<el-form-item label="执行状态" prop="statuss">
<el-select v-model="queryParams.status" placeholder="请选择执行状态" clearable>
<el-option v-for="(item, index) in com_inspection_task_status" :key="index" :value="item.value" :label="item.label"></el-option>
</el-select>
@@ -73,7 +73,11 @@
<dict-tag :options="com_inspection_task_status" :value="scope.row.status"></dict-tag>
</template>
</el-table-column>
<el-table-column label="提醒时间" align="center" prop="remindTime" />
<el-table-column label="提醒时间" width="160" align="center" prop="remindTime">
<template #default="scope">
<dict-tag :options="com_inspection_remind_tiime" :value="scope.row.remindTime"></dict-tag>
</template>
</el-table-column>
<el-table-column label="关联巡检计划" align="center" prop="planId" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
@@ -96,7 +100,7 @@ import { checkPermi } from '@/utils/permission';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_inspection_task_status } = toRefs<any>(proxy?.useDict('com_inspection_task_status'));
const { com_inspection_plan_setting } = toRefs<any>(proxy?.useDict('com_inspection_plan_setting'));
const { com_inspection_remind_tiime } = toRefs<any>(proxy?.useDict('com_inspection_remind_tiime'));
const router = useRouter();
const taskList = ref<TaskVO[]>([]);
const buttonLoading = ref(false);
@@ -118,16 +122,7 @@ const dialog = reactive<DialogOption>({
const initFormData: TaskForm = {
id: undefined,
taskName: undefined,
planId: undefined,
inspectorId: undefined,
routeId: undefined,
taskTime: undefined,
costUnit: undefined,
status: undefined,
actualStartTime: undefined,
actualEndTime: undefined,
remark: undefined,
remindTime: undefined
status: undefined
};
const data = reactive<PageData<TaskForm, TaskQuery>>({
form: { ...initFormData },
@@ -135,16 +130,7 @@ const data = reactive<PageData<TaskForm, TaskQuery>>({
pageNum: 1,
pageSize: 10,
taskName: undefined,
planId: undefined,
inspectorId: undefined,
routeId: undefined,
taskTime: undefined,
costUnit: undefined,
status: undefined,
actualStartTime: undefined,
actualEndTime: undefined,
remindTime: undefined,
params: {}
status: undefined
},
rules: {
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],

View File

@@ -64,7 +64,6 @@
<el-col :span="1.5">
<el-button v-hasPermi="['system:ossConfig:list']" type="info" plain icon="Operation" @click="handleOssConfig">配置管理</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
@@ -81,7 +80,7 @@
<el-table-column v-if="false" label="对象存储主键" align="center" prop="ossId" />
<el-table-column label="文件名" align="center" prop="fileName" />
<el-table-column label="原名" align="center" prop="originalName" />
<el-table-column label="文件后缀" align="center" prop="fileSuffix" />
<el-table-column label="文件后缀" align="center" prop="fileSuffix" width="100" />
<el-table-column label="文件展示" align="center" prop="url">
<template #default="scope">
<ImagePreview
@@ -94,14 +93,14 @@
<span v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource" v-text="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180" sortable="custom">
<el-table-column label="创建时间" align="center" prop="createTime" width="120" sortable="custom">
<template #default="scope">
<span>{{ proxy.parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="上传人" align="center" prop="createByName" />
<el-table-column label="服务商" align="center" prop="service" sortable="custom" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="上传人" align="center" prop="createByName" width="100" />
<el-table-column label="服务商" align="center" prop="service" sortable="custom" width="100" />
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="下载" placement="top">
<el-button v-hasPermi="['system:oss:download']" link type="primary" icon="Download" @click="handleDownload(scope.row)"></el-button>
@@ -152,7 +151,7 @@ const multiple = ref(true);
const total = ref(0);
const type = ref(0);
const previewListResource = ref(true);
const dateRangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
const dateRangeCreateTime = ref<[string, string]>(['', '']);
const dialog = reactive<DialogOption>({
visible: false,

View File

@@ -97,7 +97,7 @@ const form = ref({
parkingArea: null, // 车位面积
parkingFloor: null, // 车位楼层
type: '0', // 车位类型
residentId: null, // 住户id
residentId: '', // 住户id
parkingStatus: '', // 车位状态
remark: '', // 备注
parkingCode: '' // 车位编码

View File

@@ -83,7 +83,9 @@
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" @click="handleMain(scope.row)" v-hasPermi="['parking:parking:query']">审核</el-button>
<el-button link type="primary" v-if="scope.row.checkStatus === '0'" @click="handleMain(scope.row)" v-hasPermi="['parking:parking:query']"
>审核</el-button
>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['parking:parking:edit']">编辑</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['parking:parking:remove']">删除</el-button>
</template>

View File

@@ -15,43 +15,47 @@
<div class="w-full h-full CarinfoBox gridbox">
<div class="carinfo_item">
<div class="label">区域</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.areaName }}</div>
</div>
<div class="carinfo_item">
<div class="label">车位编号</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.parkingCode }}</div>
</div>
<div class="carinfo_item">
<div class="label">车位面积</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.parkingArea }} </div>
</div>
<div class="carinfo_item">
<div class="label">楼层</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.parkingFloor }} </div>
</div>
<div class="carinfo_item">
<div class="label">车位类型</div>
<div class="main">地下停车场A区</div>
<div class="main">
<dict-tag :options="com_parking_type" :value="parkingReviewinfo.parking.type" />
</div>
</div>
<div class="carinfo_item">
<div class="label">车位状态</div>
<div class="main">地下停车场A区</div>
<div class="main">
<DictTag :options="com_parking_status" :value="parkingReviewinfo.parking.parkingStatus"></DictTag>
</div>
</div>
<div class="carinfo_item">
<div class="label">持有人</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.residentId }}</div>
</div>
<div class="carinfo_item">
<div class="label">添加时间</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.createTime }}</div>
</div>
<div class="carinfo_item">
<div class="label">添加人</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.createName }}</div>
</div>
<div class="carinfo_item">
<div class="label">备注</div>
<div class="main">地下停车场A区</div>
<div class="main">{{ parkingReviewinfo.parking.remark }}</div>
</div>
</div>
</el-card>
@@ -64,7 +68,7 @@
<span>车辆信息</span>
</div>
</template>
<div class="w-full h-full CarinfoBox">
<div class="w-full h-full CarinfoBox" v-if="parkingReviewinfo.car">
<el-row>
<el-col :span="8">
<div class="carinfo_item">
@@ -114,6 +118,9 @@
</el-col>
</el-row>
</div>
<div class="w-full h-full CarinfoBox flex flex-center min-h-17rem" v-else>
<span style="color: #999; font-weight: 500; font-size: larger; letter-spacing: 10px">暂无信息</span>
</div>
</el-card>
</div>
<div class="">
@@ -148,21 +155,21 @@
</div>
</template>
<div class="w-full h-50%">
<!-- <el-steps space="200px" direction="vertical" :active="activeStop">
<el-step :description="reviewInfo.submitTime">
<el-steps space="200px" direction="vertical" :active="activeStop">
<el-step :description="parkingReviewinfo.parkingReviewProcess.submitTime">
<template #title>
<span v-if="active === 0">未提交信息</span>
<span v-if="active >= 1">已提交信息</span>
</template>
</el-step>
<el-step :description="reviewInfo.propertyTime">
<el-step :description="parkingReviewinfo.parkingReviewProcess.propertyTime">
<template #title>
<span v-if="active <= 2">待审核</span>
<span v-if="active === 3">物业审核不通过</span>
<span v-if="active >= 4">物业审核通过</span>
</template>
</el-step>
<el-step :description="reviewInfo.certificationTime">
<el-step :description="parkingReviewinfo.parkingReviewProcess.certificationTime">
<template #title>
<span v-if="active <= 5">未认证</span>
<span v-if="active === 6">已认证</span>
@@ -173,7 +180,7 @@
<span>审核完成</span>
</template>
</el-step>
</el-steps> -->
</el-steps>
</div>
</el-card>
</el-col>
@@ -184,6 +191,12 @@
<script setup lang="ts">
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { getParkingReviewInfo } from '@/api/system/parking';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_parking_status } = toRefs<any>(proxy?.useDict('com_parking_status'));
const { com_parking_type } = toRefs<any>(proxy?.useDict('com_parking_type'));
/** 住户审核状态 */
enum reviewStatus {
/** 未提交 */
@@ -202,10 +215,61 @@ enum reviewStatus {
Certification = 6
}
type activeType = reviewStatus;
const active = ref<activeType>(6);
const activeStop = ref(4);
const active = ref<activeType>(0);
const activeStop = ref(0);
const router = useRouter();
const route = useRoute();
const id = ref('');
const parkingReviewinfo = ref({
parking: {
'id': '',
'areaId': '',
'areaName': '',
'villageId': '',
'villageName': '',
'parkingCode': '',
'parkingArea': null,
'parkingStatus': '',
'type': '',
'residentId': null,
'residentName': null,
'parkingFloor': '',
'createName': '',
'checkStatus': '',
'remark': '',
'createTime': '',
'delFlag': null
},
car: null,
parkingReviewProcess: {
submitStatus: null,
propertyStatus: null,
certificationStatus: null,
submitTime: null,
propertyTime: null,
certificationTime: null
}
});
function getInfo() {
if (route.query.id) {
id.value = route.query.id as string;
getParkingReviewInfo(id.value).then((res) => {
parkingReviewinfo.value = res.data;
handleActivestaus(res.data.parkingReviewProcess);
});
}
}
getInfo();
function handleActivestaus(rews) {
if (!rews) {
activeStop.value = 0;
active.value = 0;
return;
}
if (rews.submitStatus === reviewStatus.unSubmit) {
activeStop.value = 0;
active.value = 0;
@@ -231,7 +295,6 @@ function handleActivestaus(rews) {
}
}
const router = useRouter();
function handlBacK() {
router.back();
}

View File

@@ -41,7 +41,7 @@
<div class="userinfo_item img">
<div class="title_item">身份证正面</div>
<div class="userinfo_itme_img">
<el-image class="w-full h-full" fit="fill" :src="splitImages(userInfo.cardImageFront)"></el-image>
<el-image class="w-full h-full" fit="fill" :src="userInfo.cardImageFront"></el-image>
</div>
</div>
</el-col>
@@ -52,7 +52,7 @@
</div>
<div class="userinfo_item">
<div class="title_item">住户性别</div>
<span> {{ userInfo.gender === 1 ? '男' : '女' }} </span>
<span> {{ userInfo.gender === '0' ? '男' : '女' }} </span>
</div>
<div class="userinfo_item">
<div class="title_item">证件号码</div>
@@ -117,7 +117,7 @@
</el-step>
<el-step :description="reviewInfo.propertyTime">
<template #title>
<DictTag :options="com_review" :value="reviewInfo.propertyStatus || '0'"></DictTag>
<span>{{ getReviewStatusLabel(reviewInfo.propertyStatus || '0') }}</span>
</template>
</el-step>
<el-step :description="reviewInfo.certificationTime">
@@ -155,6 +155,7 @@ const userInfo = ref<ResidentVO>({
code: null,
villageId: '',
buildingId: '',
userId: null,
'id': null,
'name': '',
'type': null,
@@ -198,7 +199,11 @@ if (route.query.id) {
});
});
}
// 在 script setup 中添加
const getReviewStatusLabel = (status: string) => {
const item = com_review.value?.find((i: any) => i.value === status);
return item ? item.label : '未知状态';
};
function handleActivestaus(rews) {
// 提交状态 0未提交 1已提交
if (rews.submitStatus === '0') {

View File

@@ -27,7 +27,7 @@
<el-card style="flex: 1" shadow="never">
<div class="residentBody h-full">
<div class="treebox h-full">
<div class="treeTitlebox">{{ currentVilageInfo.villageName ?? '12' }}</div>
<div class="treeTitlebox">{{ currentVilageInfo.villageName ?? '--' }}</div>
<div class="treeboxbody">
<!-- 树状+ -->
<el-tree :highlight-current="true" style="max-width: 600px" :data="treedata" :props="defaultProps" @node-click="handleNodeClick" />
@@ -152,7 +152,6 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const initFormData: ResidentForm = {
id: undefined,
code: undefined,
name: undefined,
type: undefined,
@@ -372,6 +371,7 @@ onMounted(() => {
display: flex;
.treebox {
width: 250px;
min-width: 250px;
margin-right: 10px;
.treeTitlebox {
height: 60px;

View File

@@ -57,14 +57,11 @@
</template>
<script setup lang="ts">
import api from '@/api/system/user';
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { getCommunitylistAPI } from '@/api/system/community';
import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index';
import { addRole, getRole, updateRole } from '@/api/system/role';
import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
import { DeptTreeOption } from '@/api/system/role/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
@@ -102,6 +99,7 @@ const rules = ref({
async function init() {
if (route.query.id) {
userid.value = route.query.id;
const { data } = await getRole(userid.value);
Object.assign(form.value, data);
form.value.roleSort = Number(form.value.roleSort);
@@ -129,7 +127,7 @@ const getRoleMenuTreeselect = (roleId: string | number) => {
};
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: boolean, type: string) => {
const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
if (type == 'menu') {
const treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) {
@@ -147,7 +145,7 @@ const handleCheckedTreeExpand = (value: boolean, type: string) => {
}
};
/** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any, type: string) => {
const handleCheckedTreeNodeAll = (value: CheckboxValueType, type: string) => {
if (type == 'menu') {
menuRef.value?.setCheckedNodes(value ? (menuOptions.value as any) : []);
} else if (type == 'dept') {
@@ -155,9 +153,9 @@ const handleCheckedTreeNodeAll = (value: any, type: string) => {
}
};
/** 树权限(父子联动) */
const handleCheckedTreeConnect = (value: any, type: string) => {
const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => {
if (type == 'menu') {
form.value.menuCheckStrictly = value;
form.value.menuCheckStrictly = value as boolean;
} else if (type == 'dept') {
// form.value.deptCheckStrictly = value;
}

View File

@@ -225,7 +225,7 @@ const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dateRange = ref<[string, string]>(['', '']);
const menuOptions = ref<MenuTreeOption[]>([]);
const menuExpand = ref(false);
const menuNodeAll = ref(false);
@@ -315,7 +315,7 @@ const resetQuery = () => {
/**删除按钮操作 */
const handleDelete = async (row?: RoleVO) => {
const roleids = row?.roleId || ids.value;
await proxy?.$modal.confirm('是否确认删除角色编号为' + roleids + '数据项目');
await proxy?.$modal.confirm('是否确认删除角色');
await delRole(roleids);
getList();
proxy?.$modal.msgSuccess('删除成功');
@@ -431,19 +431,19 @@ const getRoleDeptTreeSelect = async (roleId: string | number) => {
return res.data;
};
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: boolean, type: string) => {
const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
if (type == 'menu') {
const treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (menuRef.value) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
menuRef.value.store.nodesMap[treeList[i].id].expanded = value as boolean;
}
}
} else if (type == 'dept') {
const treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (deptRef.value) {
deptRef.value.store.nodesMap[treeList[i].id].expanded = value;
deptRef.value.store.nodesMap[treeList[i].id].expanded = value as boolean;
}
}
}

View File

@@ -93,6 +93,8 @@ import Echarts from '@/components/Echarts/index.vue';
import { EChartsOption } from 'echarts';
import { getLast7Days, getLastNDays } from '@/utils/time';
const date = ref('');
/** 入住情况 */
const options = shallowRef<EChartsOption>({
tooltip: { show: false },

View File

@@ -75,6 +75,7 @@ import Echarts from '@/components/Echarts/index.vue';
import { EChartsOption } from 'echarts';
import { getLast7Days, getLastNDays } from '@/utils/time';
const date = ref('');
/** 入住情况 */
const options = shallowRef<EChartsOption>({
tooltip: { show: false },

Some files were not shown because too many files have changed in this diff Show More