8/4 更新 输入限制,修复收银台

This commit is contained in:
Zy
2026-08-04 16:07:22 +08:00
parent ca5f214bac
commit 494b7869ea
26 changed files with 668 additions and 412 deletions

View File

@@ -9,9 +9,9 @@ VITE_APP_ENV='development'
# 开发环境 # 开发环境
VITE_APP_BASE_API='/dev-api' VITE_APP_BASE_API='/dev-api'
# # 开发环境 接口代理地址 # # 开发环境 接口代理地址
VITE_APP_PROXY_API='http://192.168.1.222:8080' #VITE_APP_PROXY_API='http://192.168.1.222:8080'
# 开发环境 接口代理地址 # 开发环境 接口代理地址
#VITE_APP_PROXY_API='http://192.168.1.6:8080' VITE_APP_PROXY_API='http://192.168.1.6:8080'
# 图片基础地址 # 图片基础地址
VITE_IMG_URL='http://192.168.1.222:8080' VITE_IMG_URL='http://192.168.1.222:8080'
@@ -24,7 +24,7 @@ VITE_APP_MONITOR_ADMIN='http://192.168.1.215/admin/applications'
# SnailJob 控制台地址 # SnailJob 控制台地址
VITE_APP_SNAILJOB_ADMIN='http://192.168.1.215/snail-job' VITE_APP_SNAILJOB_ADMIN='http://192.168.1.215/snail-job'
VITE_APP_PORT=5173 VITE_APP_PORT=5174
# 登录成功跳转地址 # 登录成功跳转地址
# VITE_LOGIN_BACK_URL = '/equipment/WaterDevice' # VITE_LOGIN_BACK_URL = '/equipment/WaterDevice'

View File

@@ -39,7 +39,7 @@ export const get_movein_user_type = (query?: {
'tenantRate': string; 'tenantRate': string;
'relatives': string; 'relatives': string;
'relativeRate': string; 'relativeRate': string;
'other': string; 'otherType': string;
'otherRate': string; 'otherRate': string;
'totalResident': string; 'totalResident': string;
}> => { }> => {

View File

@@ -42,6 +42,7 @@ export type billlistType = {
'endDate': string; 'endDate': string;
'money': string; 'money': string;
'payStatus': string; 'payStatus': string;
'remark': string;
amount: string; amount: string;
residentName: string; residentName: string;
}; };

View File

@@ -87,3 +87,27 @@ export const getHouseOwnUser = (houseId: string): AxiosPromise<uploadHouseImaget
method: 'GET' method: 'GET'
}); });
}; };
/**
* 房屋租赁管理 改变状态 0下架 1上架
* @param houseId
* */
export const changeHouseRedientStatus = (houseId: string): AxiosPromise<uploadHouseImagetype> => {
return request({
url: `/houseRental/changeStatus/${houseId}`,
method: 'PUT',
});
};
/**
* 房屋租赁管理 签约
* @param data
*/
export const HouseRedientSign = (data: HouseRentalForm): AxiosPromise<uploadHouseImagetype> => {
return request({
url: `/houseRental/sign`,
method: 'POST',
data: data
});
};

View File

@@ -2,7 +2,7 @@ export interface HouseRentalVO {
/** /**
* id 房屋租赁表id * id 房屋租赁表id
*/ */
id: string | number; id: string;
/** /**
* 租户姓名 * 租户姓名
@@ -72,7 +72,7 @@ export interface HouseRentalVO {
/** /**
* 房屋id * 房屋id
*/ */
houseId: number; houseId: string;
/** /**
* 房屋用途 * 房屋用途
*/ */
@@ -88,83 +88,74 @@ export interface HouseRentalVO {
pubTime: string; pubTime: string;
houseImageUrls: string[]; houseImageUrls: string[];
facilitieslist: string[]; facilitieslist: string[];
signingList?: HouseRentalSigningVO[];
} }
export interface HouseRentalForm extends BaseEntity { export interface HouseRentalForm extends BaseEntity {
/** /**
* id 房屋租赁表id * 收费模式
*/ */
id?: string | number; chargeMode: string;
/** /**
* 租户姓名 * 收费标准
*/ */
rentalName?: string; chargeStandard: string;
/** /**
* 租金 单位元 * 合同编号
*/ */
rent?: number; contractNo: string;
/** /**
* 0 整租 1 合租 * 合同状态
*/ */
rentalType?: string; contractStatus: string;
/**
* 房屋朝向
*/
houseFace?: string;
/**
* 房屋面积
*/
houseArea?: number;
/**
* 楼层号
*/
floorNo?: number;
/**
* 入住时间
*/
inTime?: string;
/**
* 房屋设施(“,”隔开)
*/
facilities?: string;
/**
* 房屋照片url( "," 隔开)
*/
houseImageUrl?: string;
/**
* 补充租房信息
*/
supInfo?: string;
/**
* 手机号
*/
phone?: number;
/**
* 微信
*/
vx?: string;
/**
* 邮箱
*/
email?: string;
/** /**
* 房屋id * 房屋id
*/ */
houseId?: string | number; houseId?: string;
/**
* 租赁结束时间
*/
leaseEndDate: string;
/**
* 租赁起始时间
*/
leaseStartDate: string;
/**
* 备注
*/
remark?: string;
/**
* 房屋租赁ID
*/
rentalId: string;
/**
* 承租方姓名
*/
tenantName: string;
/**
* 承租方联系电话
*/
tenantPhone: string;
/**
* 小区id
*/
villageId?: string;
}
export interface HouseRentalSigningVO {
'id': string;
'rentalId': string;
'contractNo': string;
'tenantName': string;
'tenantPhone': string;
'chargeMode': string;
'chargeStandard': string;
'contractStatus': string;
'leaseStartDate': string;
'leaseEndDate': string;
'houseId': string;
'villageId': string;
'remark': string;
'createTime': string;
} }
export interface HouseRentalQuery extends PageQuery { export interface HouseRentalQuery extends PageQuery {

View File

@@ -77,6 +77,10 @@ export interface ResidentVO {
} }
export interface ResidentForm extends BaseEntity { export interface ResidentForm extends BaseEntity {
/**
* 年龄
*/
age?: number;
/** /**
* 主键 住户管理id * 主键 住户管理id
*/ */
@@ -163,17 +167,24 @@ export interface ResidentQuery extends PageQuery {
* 日期范围参数 * 日期范围参数
*/ */
date?: any; date?: any;
/** /**
* 0业主 * 房屋名称
* 1亲属 */
* 2租户 houseName?: string;
* 3朋友 /**
* 姓名
*/
name?: string;
/**
* 0 业主
* 1 亲属
* 2 租户
* 3 朋友
*/ */
ownerRelationship?: string; ownerRelationship?: string;
/** /**
* 住户状态 * 住户状态
* 0审核中 1 已认证 * 0 审核中 1 已认证
*/ */
status?: string; status?: string;

View File

@@ -1,40 +1,3 @@
/**
* 验证身份证号码18位支持尾号x/X
* @param idCard 身份证号
* @return {boolean} true 符合 false 不符合
*/
export function validateIdCard(idCard: string): boolean {
// 18位身份证正则
const reg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return reg.test(idCard.trim());
}
/**
* 验证车牌号
* @param val 兼容新能源 + 普通车牌
* @return {boolean} true 符合 false 不符合
*/
export function isPlateNumber(val: string): boolean {
const reg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/;
return reg.test(val);
}
/**
* 验证金钱
* @return {boolean} true 符合 false 不符合
* @param value
*/
export function isMoney(value: number): boolean {
const reg = /^(0|[1-9]\d*)(\.\d{1,2})?$/;
return reg.test(String(value).trim());
}
/**
* 验证姓名
* @param {string} val
* @return {boolean} true 符合 false 不符合
*/
export function isChineseName(val: string): boolean {
return /^[\u4e00-\u9fa5·]{2,16}$/.test(String(val).trim());
}
const reg = { const reg = {
/** 金钱 */ /** 金钱 */
money: /^(0|[1-9]\d*)(\.\d{1,2})?$/, money: /^(0|[1-9]\d*)(\.\d{1,2})?$/,
@@ -43,7 +6,11 @@ const reg = {
/** 车牌 */ /** 车牌 */
PlateNumber: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/, PlateNumber: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/,
/** 姓名 */ /** 姓名 */
ChineseName: /^[\u4e00-\u9fa5·]{2,16}$/, ChineseName: /^[\u4e00-\u9fa5]{1,16}$/,
/** 字符 除了颜文字emoji表情 */
Character: /^[a-zA-Z0-9_\u4e00-\u9fa5-]+$/,
/** 联系方式 */
contact: /^[a-zA-Z0-9_\u4e00-\u9fa5-@.]+$/,
/** 手机号 */ /** 手机号 */
phone: /^1[3-9]\d{9}$/, phone: /^1[3-9]\d{9}$/,
/** 邮箱 */ /** 邮箱 */
@@ -55,7 +22,14 @@ const reg = {
/** 护照 */ /** 护照 */
passport: /^[EG]\d{8}$/, passport: /^[EG]\d{8}$/,
/** 港澳通行证 */ /** 港澳通行证 */
hongkongPassport: /^[CHMW]\d{8}$/ hongkongPassport: /^[CHMW]\d{8}$/,
/** 年龄 */
age: /^[0-9]\d{0,2}$/,
/** 数字 */
number: /^[0-9]+$/,
// 完整版所有emoji+肤色修饰+组合连接符+全部颜文字符号
fullFilterReg:
/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{FE00}-\u{FE0F}\u{1F900}-\u{1F9FF}\u{1F3FB}-\u{1F3FF}\u{200D}︿-]/gu
}; };
// 自动推导 TS 类型(最标准) // 自动推导 TS 类型(最标准)
export type RegType = keyof typeof reg; export type RegType = keyof typeof reg;
@@ -68,149 +42,3 @@ export type RegType = keyof typeof reg;
export function validator(value: string | number, regType: RegType): boolean { export function validator(value: string | number, regType: RegType): boolean {
return reg[regType].test(String(value)); return reg[regType].test(String(value));
} }
/**
* 车牌验证工具类
* 支持传统燃油、新能源、军警、武警、使馆、临时、港澳、挂车等全量车牌
*/
export class LicensePlateValidator {
/**
* 省份简称正则片段
*/
private static readonly PROVINCE = '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]';
/**
* 验证传统燃油车牌 (蓝牌/黄牌)
* 格式: 省份 + 字母 + 5位字符 (数字或字母排除I,O)
* 示例: 京A·12345, 粤B·88888
* 总长7位
*/
static isTraditionalPlate(plate: string): boolean {
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][A-HJ-NP-Z0-9]{5}$`);
if (!reg.test(plate.toUpperCase())) return false;
return true;
}
/**
* 验证新能源车牌 (绿牌)
* 规则:除了标识位 D/F 外,其余序号位必须为数字 (0-9)
* 小型: 省份 + 字母 + D/F + 5位数字 或 省份 + 字母 + 5位数字 + D/F
* 大型: 省份 + 字母 + 5位数字 + D/F
* 总长8位
*/
static isNewEnergyPlate(plate: string): boolean {
// 修正非D/F位仅限数字
// 1. [省份][A-HJ-NP-Z][D-F][0-9]{5} (D/F在序号第1位后跟5位数字)
// 2. [省份][A-HJ-NP-Z][0-9]{5}[D-F] (D/F在序号最后1位前跟5位数字)
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z](?:[D-F][0-9]{5}|[0-9]{5}[D-F])$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证警车车牌
* 格式: 省份 + 字母 + 4位数字 + 警
* 总长7位
*/
static isPolicePlate(plate: string): boolean {
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][0-9]{4}警$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证教练车牌
* 格式: 省份 + 字母 + 4位数字 + 学
* 总长7位
*/
static isCoachPlate(plate: string): boolean {
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][0-9]{4}学$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证挂车车牌
* 格式: 省份 + 字母 + 4位数字 + 挂
* 总长7位
*/
static isTrailerPlate(plate: string): boolean {
const reg = new RegExp(`^${this.PROVINCE}[A-HJ-NP-Z][0-9]{4}挂$`);
return reg.test(plate.toUpperCase());
}
/**
* 验证武警车牌 (2013式)
* 格式: WJ + 省份/部门简称 + 5位字符
* 总长8位 (WJ+1+5)
*/
static isArmedPolicePlate(plate: string): boolean {
const reg = /^WJ(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]|[A-HJ-NP-Z])[0-9A-HJ-NP-Z]{5}$/;
return reg.test(plate.toUpperCase());
}
/**
* 验证军队车牌 (2012式)
* 格式: 军种字母 + 字母/数字 + 5位字符
* 总长7位
*/
static isMilitaryPlate(plate: string): boolean {
const reg = /^[VKBHSLJNGCMPEOA][A-HJ-NP-Z0-9][0-9A-HJ-NP-Z]{4}$/;
return reg.test(plate.toUpperCase());
}
/**
* 验证使馆/领馆车牌
* 格式: 使/领 + 6位数字
* 总长7位
*/
static isEmbassyPlate(plate: string): boolean {
const reg = /^[使领][0-9]{6}$/;
return reg.test(plate);
}
/**
* 验证港澳入出境车 (粤Z牌)
* 格式: 粤Z + 4位字符 + 港/澳
* 总长7位
*/
static isHKMacauPlate(plate: string): boolean {
const reg = /^粤Z[A-HJ-NP-Z0-9]{4}[港澳]$/;
return reg.test(plate.toUpperCase());
}
/**
* 验证临时车牌
* 格式: 省份 + 字母 + 5位字符 + 临 / 超
* 总长8位
*/
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, ''); // 去除分隔符和空格
// 长度预检查:车牌只能是 7 位或 8 位
if (upperPlate.length !== 7 && upperPlate.length !== 8) {
return false;
}
return (
this.isTraditionalPlate(upperPlate) ||
this.isNewEnergyPlate(upperPlate) ||
this.isPolicePlate(upperPlate) ||
this.isCoachPlate(upperPlate) ||
this.isTrailerPlate(upperPlate) ||
this.isArmedPolicePlate(upperPlate) ||
this.isMilitaryPlate(upperPlate) ||
this.isEmbassyPlate(upperPlate) ||
this.isHKMacauPlate(upperPlate) ||
this.isTemporaryPlate(upperPlate)
);
}
}

View File

@@ -43,7 +43,8 @@ const whiteVillageId = [
'/payRecordList/payRecordList/list', '/payRecordList/payRecordList/list',
'/warehouse', '/warehouse',
'/notification/read', // 已读 '/notification/read', // 已读
'/cash/billList' '/cash/billList',
'/houseRental/changeStatus'
// '/building/list' // '/building/list'
]; ];

View File

@@ -56,8 +56,8 @@
<el-col :span="11"> <el-col :span="11">
<el-form-item label="报名截止日期" prop="deadline"> <el-form-item label="报名截止日期" prop="deadline">
<el-date-picker <el-date-picker
format="YYYY-MM-DD" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss"
v-model="deadline" v-model="deadline"
@update:model-value=" @update:model-value="
(data) => { (data) => {
@@ -68,7 +68,7 @@
} }
} }
" "
type="date" type="datetime"
placeholder="请选择报名截止日期" placeholder="请选择报名截止日期"
/> />
</el-form-item> </el-form-item>
@@ -115,6 +115,7 @@
:on-exceed="handleExceed" :on-exceed="handleExceed"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
:on-error="handleError" :on-error="handleError"
accept=".png,.jpg,.jpeg,.bmp"
:limit="1" :limit="1"
> >
<img alt="活动封面" v-if="form.coverImage" :src="form.coverImage" class="avatar" /> <img alt="活动封面" v-if="form.coverImage" :src="form.coverImage" class="avatar" />
@@ -225,6 +226,7 @@ import { addActivity, getActivity, updateActivity } from '@/api/system/Activity'
import { genFileId, UploadInstance, UploadProps, UploadRawFile } from 'element-plus'; import { genFileId, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
import AMapLoader from '@amap/amap-jsapi-loader'; import AMapLoader from '@amap/amap-jsapi-loader';
import { Plus } from '@element-plus/icons-vue'; import { Plus } from '@element-plus/icons-vue';
import { validator } from '@/utils/Reg';
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/activity/uploadImage'; const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/activity/uploadImage';
@@ -278,13 +280,34 @@ const form = ref({
}); });
const rules = { const rules = {
title: [{ required: true, message: '请输入活动标题', trigger: 'blur' }], title: [{ required: true, message: '请输入活动标题', trigger: 'blur' }],
author: [{ required: true, message: '请输入作者', trigger: 'blur' }], author: [
{ required: true, message: '请输入作者', trigger: 'blur' },
{
validator: (_: any, value: string | number) => validator(value, 'Character'),
message: '请输入中文,字母,数字,-,_等字符',
trigger: 'blur'
}
],
applyDeadline: [{ required: true, message: '请输入报名截止日期', trigger: 'blur' }], applyDeadline: [{ required: true, message: '请输入报名截止日期', trigger: 'blur' }],
startTime: [{ required: true, message: '请选择开始日期', trigger: 'blur' }], startTime: [{ required: true, message: '请选择开始日期', trigger: 'blur' }],
endTime: [{ required: true, message: '请选择结束日期', trigger: 'blur' }], endTime: [{ required: true, message: '请选择结束日期', trigger: 'blur' }],
address: [{ required: true, message: '请输入活动地点', trigger: 'blur' }], address: [{ required: true, message: '请输入活动地点', trigger: 'blur' }],
organizer: [{ required: true, message: '请输入主办方', trigger: 'blur' }], organizer: [
contactName: [{ required: true, message: '请输入联系人', trigger: 'blur' }], { required: true, message: '请输入主办方', trigger: 'blur' },
{
validator: (_: any, value: string | number) => validator(value, 'Character'),
message: '请输入中文,字母,数字,-,_等字符',
trigger: 'blur'
}
],
contactName: [
{ required: true, message: '请输入联系人', trigger: 'blur' },
{
validator: (_: any, value: string | number) => validator(value, 'Character'),
message: '请输入中文,字母,数字,-,_等字符',
trigger: 'blur'
}
],
contactPhone: [{ required: true, message: '请输入联系方式', trigger: 'blur' }], contactPhone: [{ required: true, message: '请输入联系方式', trigger: 'blur' }],
coverImage: [{ required: true, message: '请上传活动封面', trigger: 'blur' }], coverImage: [{ required: true, message: '请上传活动封面', trigger: 'blur' }],
content: [{ required: true, message: '请输入内容', trigger: 'blur' }] content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
@@ -301,10 +324,11 @@ const activityTime = ref([]);
const upload = ref<UploadInstance>(); const upload = ref<UploadInstance>();
// 上传成功 // 上传成功
const handleAvatarSuccess: UploadProps['onSuccess'] = (response) => { const handleAvatarSuccess: UploadProps['onSuccess'] = (response) => {
console.log(response);
if (response.data && response.data.url) { if (response.data && response.data.url) {
form.value.coverImage = response.data.url; form.value.coverImage = response.data.url;
} else { } else {
ElMessage.warning('上传频繁,请稍后再试'); ElMessage.warning(response.msg);
} }
}; };
@@ -320,7 +344,7 @@ const handleExceed: UploadProps['onExceed'] = (files) => {
const handleError: UploadProps['onError'] = (error: Error) => { const handleError: UploadProps['onError'] = (error: Error) => {
console.log(error); console.log(error);
}; };
const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic']; const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
/** 检测文件大小和格式 */ /** 检测文件大小和格式 */
const handleBeforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => { const handleBeforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
if (!allowImgSuffix.includes(rawFile.name.split('.')[1])) { if (!allowImgSuffix.includes(rawFile.name.split('.')[1])) {
@@ -342,7 +366,7 @@ let geocoder: any = null;
const mapReady = ref(false); const mapReady = ref(false);
let mouseTool = null; let mouseTool = null;
// 0 开始 1编辑 2结束 // 0 开始 1 编辑 2结束
const isDraw = ref(0); const isDraw = ref(0);
onMounted(() => { onMounted(() => {

View File

@@ -80,6 +80,17 @@ const form = ref({
const rules = { const rules = {
houseId: [{ required: true, message: '请选择具体房屋', trigger: 'blur' }], houseId: [{ required: true, message: '请选择具体房屋', trigger: 'blur' }],
applyName: [{ required: true, message: '请输入申请人', trigger: 'blur' }], applyName: [{ required: true, message: '请输入申请人', trigger: 'blur' }],
decorationCompany: [
{ required: true, message: '请输入装修公司名称', trigger: 'blur' },
{
validator: (_: any, value: string | number, callback: any) => {
if (value === '' || value === '') {
return callback();
}
return !validator(value, 'fullFilterReg') ? callback() : callback(new Error('请输入正确的装修公司名称'));
}
}
],
decorationDeposit: [ decorationDeposit: [
{ required: true, message: '请输入装修押金', trigger: 'blur' }, { required: true, message: '请输入装修押金', trigger: 'blur' },
{ {
@@ -103,7 +114,17 @@ const rules = {
trigger: ['blur', 'change'] trigger: ['blur', 'change']
} }
], ],
decorationContent: [{ required: true, message: '请输入装修内容', trigger: 'blur' }] decorationContent: [
{ required: true, message: '请输入装修内容', trigger: 'blur' },
{
validator: (_: any, value: string | number, callback: any) => {
if (value === '' || value === '') {
return callback();
}
return !validator(value, 'fullFilterReg') ? callback() : callback(new Error('请输入正确的装修内容'));
}
}
]
}; };
const userid = ref(); const userid = ref();

View File

@@ -112,7 +112,8 @@ import { addHandleLog, getHandleLog, updateHandleLog, uploadAttachments } from '
import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'; import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { Upload } from '@element-plus/icons-vue'; // 确保引入图标组件╬╬╬ import { Upload } from '@element-plus/icons-vue';
import { validator } from '@/utils/Reg'; // 确保引入图标组件
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/handleLog/uploadAttachments'; const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/handleLog/uploadAttachments';
const route = useRoute(); const route = useRoute();
@@ -129,7 +130,25 @@ const form = ref({
}); });
const rules = { const rules = {
content: [{ required: true, message: '请输入办事内容', trigger: 'blur' }], content: [{ required: true, message: '请输入办事内容', trigger: 'blur' }],
seekName: [{ required: true, message: '请选择求助人', trigger: 'blur' }] seekName: [
{ required: true, message: '请选择求助人', trigger: 'blur' },
{
validator: (_: any, value: string | number) => validator(value, 'Character'),
message: '请输入中文,字母,数字,-,_等字符',
trigger: 'blur'
}
],
phone: [
{ required: false, message: '请输入联系方式', trigger: 'blur' },
{
validator: (_: any, value: string | number, callback: any) => {
if (value === '' || value === '') {
return callback();
}
return !validator(value, 'fullFilterReg') ? callback() : callback(new Error('请输入正确的联系方式'));
}
}
]
}; };
const userid = ref(); const userid = ref();

View File

@@ -99,7 +99,6 @@ import PageHeader from '@/components/Pageheader/index.vue';
import { addMonthCard, getMonthCard, updateMonthCard } from '@/api/system/MonthCard'; import { addMonthCard, getMonthCard, updateMonthCard } from '@/api/system/MonthCard';
import { validator } from '@/utils/Reg'; import { validator } from '@/utils/Reg';
import { LicensePlateValidator2 } from '@/utils/Reg2'; import { LicensePlateValidator2 } from '@/utils/Reg2';
import { callback } from '@/api/login';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_pay_method } = toRefs<any>(proxy?.useDict('com_pay_method')); const { com_pay_method } = toRefs<any>(proxy?.useDict('com_pay_method'));
@@ -126,7 +125,14 @@ function handleDate() {
form.value.endTime = checkDate.value[1]; form.value.endTime = checkDate.value[1];
} }
const rules = ref({ const rules = ref({
cardCode: [{ required: true, message: '请输入月卡编号', trigger: 'blur' }], cardCode: [
{ required: true, message: '请输入月卡编号', trigger: 'blur' },
{
validator: (_: any, value: string | number) => validator(value, 'Character'),
message: '请输入中文,字母,数字,-,_等字符',
trigger: 'blur'
}
],
cardName: [ cardName: [
{ required: true, message: '请输入姓名', trigger: 'blur' }, { required: true, message: '请输入姓名', trigger: 'blur' },
{ {

View File

@@ -23,6 +23,7 @@
</el-form-item> </el-form-item>
<el-form-item label="起止日期" prop="price"> <el-form-item label="起止日期" prop="price">
<el-date-picker <el-date-picker
:disabled="!Boolean(form.recordMonth)"
v-model="StartEndDate" v-model="StartEndDate"
@update:model-value="handleDate" @update:model-value="handleDate"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"

View File

@@ -21,7 +21,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item @change="handleChangeHouseNo" label="房号" prop="houseId"> <el-form-item @change="handleChangeHouseNo" label="房号" prop="houseId">
<el-select v-model="queryParams.houseId" placeholder="请选择收费项目"> <el-select v-model="queryParams.houseId" placeholder="请选择房号">
<el-option-group v-for="group in houseList" :key="group.floorNo" :label="String(group.floorNo) + ' 楼'"> <el-option-group v-for="group in houseList" :key="group.floorNo" :label="String(group.floorNo) + ' 楼'">
<el-option v-for="item in group.houses" :key="item.houseId" :label="item.houseNo" :value="item.houseId" /> <el-option v-for="item in group.houses" :key="item.houseId" :label="item.houseNo" :value="item.houseId" />
</el-option-group> </el-option-group>
@@ -30,7 +30,7 @@
</div> </div>
<div> <div>
<el-form-item label="收费项目" prop="itemId"> <el-form-item label="收费项目" prop="itemId">
<el-select v-model="queryParams.chargeItemId" placeholder="请选择收费项目"> <el-select multiple v-model="queryParams.chargeItemId" placeholder="请选择收费项目">
<el-option <el-option
:disabled="item.status === '1'" :disabled="item.status === '1'"
v-for="(item, index) in chargeItems" v-for="(item, index) in chargeItems"
@@ -147,7 +147,10 @@
<el-table-column label="应收金额" width="100" align="center" prop="amount" /> <el-table-column label="应收金额" width="100" align="center" prop="amount" />
<el-table-column label="缴费状态" width="100" align="center" prop="payStatus"> <el-table-column label="缴费状态" width="100" align="center" prop="payStatus">
<template #default="scope"> <template #default="scope">
<DictTag :options="com_pay_status" :value="scope.row.payStatus"></DictTag> <span v-if="scope.row.payStatus === '0'">未缴纳</span>
<span v-else-if="scope.row.payStatus === '1'">已缴纳</span>
<span v-else-if="scope.row.payStatus === '2'">缴纳失败</span>
<span v-else>部分缴纳</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="应缴金额" width="100" align="center" prop="amount" /> <el-table-column label="应缴金额" width="100" align="center" prop="amount" />
@@ -159,7 +162,7 @@
<el-table-column label="操作" width="110" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" width="110" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" v-if="scope.row.payStatus !== '1'" @click="handleIncome(scope.row)" v-has-permi="['bill:bill:edit']" <el-button link type="primary" v-if="scope.row.payStatus !== '1'" @click="handleIncome(scope.row)" v-has-permi="['bill:bill:edit']"
></el-button ></el-button
> >
<el-button link type="success" v-if="scope.row.payStatus === '1'" @click="printPIaoJuByOnly(scope.row)" v-has-permi="['bill:bill:query']" <el-button link type="success" v-if="scope.row.payStatus === '1'" @click="printPIaoJuByOnly(scope.row)" v-has-permi="['bill:bill:query']"
>打印票据</el-button >打印票据</el-button
@@ -180,7 +183,10 @@
<el-dialog :title="dialog.title" v-model="dialog.visible" width="650px" append-to-body> <el-dialog :title="dialog.title" v-model="dialog.visible" width="650px" append-to-body>
<div class="payInfo"> <div class="payInfo">
<div> <div>
<div>缴费人 {{ inComeFrom.residentName }}</div> <div class="flex flex-items-center justify-between">
<span>缴费人 {{ inComeFrom.residentName }}</span>
<span style="color: #ff9947">待缴纳</span>
</div>
</div> </div>
<div class="flex"> <div class="flex">
<span>收费名称 </span> <span>收费名称 </span>
@@ -204,11 +210,11 @@
<span>{{ item.amount }}</span> <span>{{ item.amount }}</span>
</div> </div>
<div style="text-align: right" class="flex justify-between"> <div style="text-align: right" class="flex justify-between">
<span>上次优惠金额</span> <span>累计优惠金额</span>
<span> {{ item.singleDiscountAmount }}</span> <span> {{ item.singleDiscountAmount }}</span>
</div> </div>
<div style="text-align: right" class="flex justify-between"> <div style="text-align: right" class="flex justify-between">
<span>上次已交金额</span> <span>累计已交金额</span>
<span> {{ item.paidAmount }}</span> <span> {{ item.paidAmount }}</span>
</div> </div>
<div <div
@@ -264,40 +270,11 @@
</el-form-item> </el-form-item>
<el-form-item label="缴纳状态" prop="payStatus"> <el-form-item label="缴纳状态" prop="payStatus">
<el-radio-group v-model="inComeFrom.payStatus"> <el-radio-group v-model="inComeFrom.payStatus">
<el-radio v-for="dict in com_pay_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-radio> <el-radio value="1" label="已缴纳"></el-radio>
<el-radio value="2" label="部分缴纳"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="支付方式" prop="payType" v-if="inComeFrom.payStatus !== '0'">
<div class="flex-1">
<div class="flex flex-items-center" v-for="(item, index) in payList" :key="index">
<div class="flex-1 flex">
<el-select @change="handleChangePayTypeId($event, index)" v-model="item.payType" placeholder="请选择">
<el-option
:disabled="isOptionDisabled(dict.value, item.payType)"
v-for="dict in com_pay_method"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
<el-input @change="(val: string) => handleChangePay(val, index)" v-model.number.trim="item.payAmount" style="margin: 0 10px">
<template #append></template>
</el-input>
</div>
<div style="width: 80px; text-align: left">
<el-icon
color="blue"
size="24"
v-if="index === payList.length - 1 && payList.length < com_pay_method.length"
@click="addPayList"
style="margin-right: 10px; cursor: pointer"
><CirclePlus
/></el-icon>
<el-icon color="red" style="cursor: pointer" size="24" v-if="payList.length > 1" @click="removePayList(index)"><Delete /></el-icon>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="优惠方式" prop="discountMethod" v-if="inComeFrom.payStatus !== '0'"> <el-form-item label="优惠方式" prop="discountMethod" v-if="inComeFrom.payStatus !== '0'">
<el-select style="width: 400px" @change="handleChangeDisCountMethod" v-model="inComeFrom.discountMethod" placeholder="请选择"> <el-select style="width: 400px" @change="handleChangeDisCountMethod" v-model="inComeFrom.discountMethod" placeholder="请选择">
<el-option label="不优惠" :value="0"></el-option> <el-option label="不优惠" :value="0"></el-option>
@@ -335,6 +312,37 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
</div> </div>
<el-form-item label="支付方式" prop="payType" v-if="inComeFrom.payStatus !== '0'">
<div class="flex-1">
<div class="flex flex-items-center" v-for="(item, index) in payList" :key="index">
<div class="flex-1 flex">
<el-select @change="handleChangePayTypeId($event, index)" v-model="item.payType" placeholder="请选择">
<el-option
:disabled="isOptionDisabled(dict.value, item.payType)"
v-for="dict in com_pay_method"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
<el-input @change="(val: string) => handleChangePay(val, index)" v-model.number.trim="item.payAmount" style="margin: 0 10px">
<template #append></template>
</el-input>
</div>
<div style="width: 80px; text-align: left">
<el-icon
color="blue"
size="24"
v-if="index === payList.length - 1 && payList.length < com_pay_method.length"
@click="addPayList"
style="margin-right: 10px; cursor: pointer"
><CirclePlus
/></el-icon>
<el-icon color="red" style="cursor: pointer" size="24" v-if="payList.length > 1" @click="removePayList(index)"><Delete /></el-icon>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input style="width: 400px" type="textarea" show-word-limit maxlength="100" :rows="5" v-model="inComeFrom.remark"></el-input> <el-input style="width: 400px" type="textarea" show-word-limit maxlength="100" :rows="5" v-model="inComeFrom.remark"></el-input>
</el-form-item> </el-form-item>
@@ -433,9 +441,7 @@ import { CirclePlus, Delete, QuestionFilled } from '@element-plus/icons-vue';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_pay_status, com_pay_method, com_charge_item_unit } = toRefs<any>( const { com_pay_method, com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_pay_method', 'com_charge_item_unit'));
proxy?.useDict('com_pay_status', 'com_pay_method', 'com_charge_item_unit')
);
const userStore = useUserStore(); const userStore = useUserStore();
const loading = ref(false); const loading = ref(false);
@@ -668,7 +674,7 @@ function initInComeFrom() {
inComeFrom.value = { inComeFrom.value = {
allCost: 0, // 总金额 allCost: 0, // 总金额
billResidentId: [], // 账单id billResidentId: [], // 账单id
payStatus: '1', // 0:未缴 1:已缴 payStatus: '1', // 0:未缴 1:已缴 2: 缴纳失败 3: 部分缴纳
payType: '2', // 0:微信 1:支付宝 2 线下 payType: '2', // 0:微信 1:支付宝 2 线下
remark: '', remark: '',
discountMethod: 0, // 优惠方式 0 不优惠 1 比例 2 金额 discountMethod: 0, // 优惠方式 0 不优惠 1 比例 2 金额
@@ -694,7 +700,6 @@ function initInComeFrom() {
} }
// 改变优惠方式 // 改变优惠方式
function handleChangeDisCountMethod(val: number) { function handleChangeDisCountMethod(val: number) {
console.log(val);
inComeFrom.value.discountRate = '0'; // 优惠比例 inComeFrom.value.discountRate = '0'; // 优惠比例
inComeFrom.value.discount = '0.00'; // 优惠金额 inComeFrom.value.discount = '0.00'; // 优惠金额
totalReadDiscount.value = '0.00'; // 实际优惠金额 totalReadDiscount.value = '0.00'; // 实际优惠金额
@@ -718,7 +723,6 @@ function handleChangeDiscountMoney(val: string) {
} }
let discount = toBig(cleanVal).toNumber(); let discount = toBig(cleanVal).toNumber();
if (Number.isNaN(discount)) discount = 0; if (Number.isNaN(discount)) discount = 0;
console.log('折扣金额:', discount);
const allCost = inComeFrom.value.allCost ?? 0; const allCost = inComeFrom.value.allCost ?? 0;
if (discount < 0) { if (discount < 0) {
ElMessage.warning('优惠金额不能为负数'); ElMessage.warning('优惠金额不能为负数');
@@ -830,6 +834,7 @@ function handleIncome(row: billlistType | false) {
if (row) { if (row) {
inComeFrom.value.allCost = row.needPayAmount; inComeFrom.value.allCost = row.needPayAmount;
inComeFrom.value.billResidentId = [row]; inComeFrom.value.billResidentId = [row];
inComeFrom.value.remark = row.remark;
inComeFrom.value.residentName = row.residentName; inComeFrom.value.residentName = row.residentName;
totalPayAmount.value = row.needPayAmount; totalPayAmount.value = row.needPayAmount;
} else { } else {
@@ -845,6 +850,7 @@ function handleIncome(row: billlistType | false) {
ElMessage.warning('缴费人不一致,重新选择'); ElMessage.warning('缴费人不一致,重新选择');
return; return;
} else if (set.size === 1) { } else if (set.size === 1) {
inComeFrom.value.remark = '';
inComeFrom.value.residentName = ids.value[0].residentName; inComeFrom.value.residentName = ids.value[0].residentName;
ids.value.forEach((item) => { ids.value.forEach((item) => {
inComeFrom.value.billResidentId.push(item); inComeFrom.value.billResidentId.push(item);
@@ -866,12 +872,17 @@ const closer = () => {
}; };
// 提交 // 提交
const submitForm = () => { const submitForm = () => {
if (!handlePayListIsEmpt()) {
ElMessage.warning('请填写完整支付方式和支付金额');
return;
}
buttonLoading.value = true; buttonLoading.value = true;
// 部分缴纳时 不管
// 已缴纳时 判断 支付金额 和 预计支付金额 是否相等 // 已缴纳时 判断 支付金额 和 预计支付金额 是否相等
// 混合缴纳时 不管
if (inComeFrom.value.payStatus === '1') { if (inComeFrom.value.payStatus === '1') {
if (!eqMoney(totalPayAmount.value, payListAllAmount.value)) { if (!eqMoney(totalPayAmount.value, payListAllAmount.value)) {
return ElMessage.error('实际支付金额 和 预计支付金额 不相等'); buttonLoading.value = false;
return ElMessage.warning('支付金额不足,可选择 部分缴纳 方式');
} }
} }
const obj = { const obj = {
@@ -998,11 +1009,15 @@ const printOption = {
} }
`, `,
afterCloseCallback: () => { afterCloseCallback: () => {
console.log('打印结束');
isPrint.value = false; isPrint.value = false;
isLoading.value = false; isLoading.value = false;
} }
}; };
// 打印状态恢复函数
function resetPrintState() {
isPrint.value = false;
isLoading.value = false;
}
function conFimPrint() { function conFimPrint() {
isLoading.value = true; isLoading.value = true;
isPrint.value = true; isPrint.value = true;
@@ -1017,7 +1032,8 @@ function conFimPrint() {
// isPrint.value = false; // isPrint.value = false;
// isLoading.value = false; // isLoading.value = false;
// }); // });
}, 1000); resetPrintState();
}, 100);
} }
onMounted(() => { onMounted(() => {
@@ -1072,7 +1088,6 @@ const payListAllAmount = computed(() => {
}); });
// 添加支付方式 // 添加支付方式
function addPayList() { function addPayList() {
console.log(com_pay_method.value);
payList.value.push({ payList.value.push({
payType: '', payType: '',
payAmount: null payAmount: null
@@ -1104,6 +1119,27 @@ function handleChangePay(val: string, index: number = -1) {
} }
} }
} }
function handlePayListIsEmpt() {
if (payList.value.length === 0) {
ElMessage.warning('请添加支付方式');
return false;
}
const invalidIndex = payList.value.findIndex(
(item) =>
item.payType === null ||
item.payType === undefined ||
item.payType === '' ||
item.payAmount === null ||
item.payAmount === undefined ||
item.payAmount === ''
);
if (invalidIndex !== -1) {
return false;
}
return true;
}
// 校验数字 // 校验数字
function checkMoney(value: string) { function checkMoney(value: string) {
if (!value) return false; if (!value) return false;

View File

@@ -54,6 +54,7 @@
import { ref } from 'vue'; import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { addArea, getArea, updateArea } from '@/api/system/carArea'; import { addArea, getArea, updateArea } from '@/api/system/carArea';
import { validator } from '@/utils/Reg';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@@ -68,8 +69,46 @@ const form = ref({
remark: null remark: null
}); });
const rules = ref({ const rules = ref({
areaName: [{ required: true, message: '请输入区域名称', trigger: 'blur' }], areaName: [
areaCode: [{ required: true, message: '请输入区域编号', trigger: 'blur' }], { required: true, message: '请输入区域名称', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入名称'));
}
return validator(val, 'Character') ? callback() : callback(new Error('请输入中文,字母,数字,-,_等字符'));
}
}
],
parkingCount: [
{ required: false, message: '请输入车位数量', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback();
}
const value = Number(val);
if (Number.isNaN(value)) {
return callback(new Error('请输入数字'));
}
if (value < 0) {
return callback(new Error('请输入大于0的数字'));
}
return callback();
}
}
],
areaCode: [
{ required: true, message: '请输入区域编号', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入区域编号'));
}
return validator(val, 'Character') ? callback() : callback(new Error('请输入中文,字母,数字,-,_等字符'));
}
}
],
area: [ area: [
{ required: true, message: '请输入区域面积', trigger: 'blur' }, { required: true, message: '请输入区域面积', trigger: 'blur' },
{ {

View File

@@ -116,7 +116,6 @@ const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/car/uploadImage';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const formRef = ref(); const formRef = ref();
const carNumInputRef = ref();
const form = ref({ const form = ref({
id: null, id: null,
areaId: null, // 区域id areaId: null, // 区域id
@@ -158,9 +157,6 @@ const rules = ref({
], ],
carBrand: [{ required: false, message: '请输入车辆品牌', trigger: 'blur' }] carBrand: [{ required: false, message: '请输入车辆品牌', trigger: 'blur' }]
}); });
function handleValidate() {
formRef.value.validateField('carNum');
}
function headerToken() { function headerToken() {
const useStore = useUserStore(); const useStore = useUserStore();
return { return {
@@ -168,7 +164,7 @@ function headerToken() {
}; };
} }
const userid = ref(''); const userid = ref('');
const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic']; const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => { const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
const type = rawFile.type.split('/').pop(); const type = rawFile.type.split('/').pop();
if (allowImgSuffix.includes(type)) { if (allowImgSuffix.includes(type)) {
@@ -224,12 +220,6 @@ function changeAreaList(val: string) {
// !== 提交 ============================================================================= // !== 提交 =============================================================================
// 提交 // 提交
const submitForm = () => { const submitForm = () => {
const plateResult = carNumInputRef.value.validate();
if (!plateResult.valid) {
// 自动聚焦到错误字段
carNumInputRef.value.focus();
return;
}
formRef.value?.validate(async (valid: boolean) => { formRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
if (userid.value.trim() !== '') { if (userid.value.trim() !== '') {

View File

@@ -21,7 +21,7 @@
<el-option v-for="item in houseFormat" :key="item" :label="item" :value="item" /> <el-option v-for="item in houseFormat" :key="item" :label="item" :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="楼高(m)" prop="buildTail"> <el-form-item label="楼高(m)" prop="buildTall">
<el-input maxlength="4" v-model.trim="form.buildTall" placeholder="请输入楼高(m)" /> <el-input maxlength="4" v-model.trim="form.buildTall" placeholder="请输入楼高(m)" />
</el-form-item> </el-form-item>
<el-form-item label="楼栋结构" prop="buildStructure"> <el-form-item label="楼栋结构" prop="buildStructure">
@@ -46,6 +46,7 @@ import PageHeader from '@/components/Pageheader/index.vue';
import { addBuildingApi, editBuildingApi } from '@/api/system/house'; import { addBuildingApi, editBuildingApi } from '@/api/system/house';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useHouseStore } from '@/store/modules/house'; import { useHouseStore } from '@/store/modules/house';
import { validator } from '@/utils/Reg';
const houseStore = useHouseStore(); const houseStore = useHouseStore();
const houseFormat = ['北', '东北', '东', '东南', '南', '西南', '西', '西北']; const houseFormat = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
@@ -56,14 +57,37 @@ const form = ref({
buildingName: '', buildingName: '',
buildToward: '', buildToward: '',
buildTall: '', buildTall: '',
// buildingUse: '0',
buildStructure: '', buildStructure: '',
// unitCount: null,
floorCount: 1 floorCount: 1
}); });
const rules = ref({ const rules = ref({
buildingName: [{ required: true, message: '请输入楼栋名称', trigger: 'blur' }], buildingName: [
// buildingUse: [{ required: true, message: '请选择楼栋类型', trigger: 'blur' }], { required: true, message: '请输入楼栋名称', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入名称'));
}
return validator(val, 'Character') ? callback() : callback(new Error('请输入中文,字母,数字,-,_等字符'));
}
}
],
buildTall: [
{ required: false, message: '请输入楼高', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
console.log('val', val);
if (val === '') {
return callback();
}
const num = Number(val);
if (Number.isNaN(num)) {
return callback(new Error('请输入数字'));
}
return validator(num, 'number') ? callback() : callback(new Error('请输入数字'));
}
}
],
unitCount: [{ required: true, message: '请输入单元数', trigger: 'blur' }], unitCount: [{ required: true, message: '请输入单元数', trigger: 'blur' }],
floorCount: [{ required: true, message: '请输入楼层数', trigger: 'blur' }] floorCount: [{ required: true, message: '请输入楼层数', trigger: 'blur' }]
}); });
@@ -78,6 +102,7 @@ if (buildingId) {
function getBuildingById() { function getBuildingById() {
houseStore.currentBuildingInfoList.forEach((item) => { houseStore.currentBuildingInfoList.forEach((item) => {
if (item.id == buildingId) { if (item.id == buildingId) {
console.log(item);
form.value.buildingName = item.buildingName; form.value.buildingName = item.buildingName;
form.value.buildToward = item.buildToward; form.value.buildToward = item.buildToward;
// form.value.buildingUse = item.buildingUse; // form.value.buildingUse = item.buildingUse;

View File

@@ -86,6 +86,7 @@ import { GetVillageListParking } from '@/api/system/parking';
import { GetVillagelistStoreroom } from '@/api/system/storeroom'; import { GetVillagelistStoreroom } from '@/api/system/storeroom';
import { listHouseUseType_add_house } from '@/api/system/houseUse'; import { listHouseUseType_add_house } from '@/api/system/houseUse';
import { getByBUildingidtoUnitNo } from '@/api/system/houseUnit'; import { getByBUildingidtoUnitNo } from '@/api/system/houseUnit';
import { validator } from '@/utils/Reg';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@@ -93,7 +94,7 @@ const houseStore = useHouseStore();
const formRef = ref(); const formRef = ref();
const form = ref<addHouseType>({ const form = ref<addHouseType>({
buildingId: '', buildingId: '',
houseUseTypeId: '', // 房屋用途 houseUseTypeId: '', // 房屋用途
unitNoId: '', unitNoId: '',
floorNo: '', floorNo: '',
houseNo: '', // 房 间 号 houseNo: '', // 房 间 号
@@ -110,7 +111,17 @@ const rules = ref({
floorNo: [{ required: true, message: '请选择楼层', trigger: 'blur' }], floorNo: [{ required: true, message: '请选择楼层', trigger: 'blur' }],
houseUseTypeId: [{ required: true, message: '请选择房屋用途', trigger: 'blur' }], houseUseTypeId: [{ required: true, message: '请选择房屋用途', trigger: 'blur' }],
buildingName: [{ required: true, message: '请输入房间号', trigger: 'blur' }], buildingName: [{ required: true, message: '请输入房间号', trigger: 'blur' }],
houseNo: [{ required: true, message: '请输入房间号', trigger: 'blur' }], houseNo: [
{ required: true, message: '请输入房间号', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入名称'));
}
return validator(val, 'Character') ? callback() : callback(new Error('请输入中文,字母,数字,-,_等字符'));
}
}
],
buildingArea: [{ required: true, message: '请输入建筑面积', trigger: 'blur' }], buildingArea: [{ required: true, message: '请输入建筑面积', trigger: 'blur' }],
internalFloorArea: [{ required: true, message: '请输入套内面积', trigger: 'blur' }], internalFloorArea: [{ required: true, message: '请输入套内面积', trigger: 'blur' }],
CommonArea: [{ required: true, message: '请输入公摊面积', trigger: 'blur' }], CommonArea: [{ required: true, message: '请输入公摊面积', trigger: 'blur' }],
@@ -129,7 +140,7 @@ const rules = ref({
if (Number.isNaN(val)) { if (Number.isNaN(val)) {
return callback(new Error('请输入数字')); return callback(new Error('请输入数字'));
} }
if (val < 0 || val > 99999.99) { if (val < 0 || val > 99999999) {
ElMessage.error('房屋面积超出合法范围'); ElMessage.error('房屋面积超出合法范围');
return; return;
} }

View File

@@ -229,7 +229,17 @@ const form = ref({
const userHousePath = ref<CascaderValue>([]); const userHousePath = ref<CascaderValue>([]);
const rules = ref({ const rules = ref({
houseId: [{ required: true, message: '请选择房屋', trigger: 'blur' }], houseId: [{ required: true, message: '请选择房屋', trigger: 'blur' }],
rentalName: [{ required: true, message: '请输入名称', trigger: 'blur' }], rentalName: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入名称'));
}
return validator(val, 'ChineseName') ? callback() : callback(new Error('请输入中文字符'));
}
}
],
phone: [ phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' }, { required: true, message: '请输入手机号', trigger: 'blur' },
{ {
@@ -316,7 +326,7 @@ async function getTreeData() {
}; };
if (form.value.facilities) houseDevicesList.value = form.value.facilities.split(',').filter((item) => item); if (form.value.facilities) houseDevicesList.value = form.value.facilities.split(',').filter((item) => item);
if (form.value.houseImageUrl) fileList.value = splitStringUrl(form.value.houseImageUrl); if (form.value.houseImageUrl) fileList.value = splitStringUrl(form.value.houseImageUrl);
handleChange([form.value.buildingId, form.value.unitNoId, form.value.houseId]); userHousePath.value = [form.value.buildingId, form.value.unitNoId, form.value.houseId];
}); });
} }
} }

View File

@@ -5,7 +5,7 @@
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card> <el-card>
<div class="h-full flex flex-align-center"> <div class="h-full flex flex-align-center">
<el-form label-width="120px" ref="queryFormRef" :model="queryParams" :inline="true"> <el-form label-width="80px" ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="房屋类型" prop="houseUse"> <el-form-item label="房屋类型" prop="houseUse">
<el-select v-model="queryParams.houseUse" placeholder="选择房屋类型" style="width: 240px"> <el-select v-model="queryParams.houseUse" placeholder="选择房屋类型" style="width: 240px">
<el-option v-for="item in houseUseTypeList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in houseUseTypeList" :key="item.id" :label="item.name" :value="item.id" />
@@ -53,7 +53,6 @@
<el-table-column label="发布时间" align="center" prop="pubTime" /> <el-table-column label="发布时间" align="center" prop="pubTime" />
<el-table-column label="房屋类型" align="center" prop="houseUse"> <el-table-column label="房屋类型" align="center" prop="houseUse">
<template #default="{ row }"> <template #default="{ row }">
<!-- <DictTag :options="com_house_use" :value="row.houseUse"></DictTag>-->
<span>{{ handleHouseUse(row.houseUse) }}</span> <span>{{ handleHouseUse(row.houseUse) }}</span>
</template> </template>
</el-table-column> </el-table-column>
@@ -62,8 +61,25 @@
<DictTag :options="com_rental_method" :value="row.rentalType"></DictTag> <DictTag :options="com_rental_method" :value="row.rentalType"></DictTag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template #default="{ row }">
<span>{{ row.status === '0' ? '下架' : '上架' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" v-if="scope.row.status === '0'" @click="sign(scope.row)" v-has-permi="['houseRental:houseRental:edit']"
>签约</el-button
>
<el-button
link
type="primary"
v-if="scope.row.status === '0'"
@click="putOnSale(scope.row)"
v-has-permi="['houseRental:houseRental:edit']"
>上架</el-button
>
<el-button link type="primary" v-else @click="putOnSale(scope.row)" v-has-permi="['houseRental:houseRental:edit']">下架</el-button>
<el-button link type="primary" @click="handleMain(scope.row)" v-has-permi="['houseRental:houseRental:edit']">详情</el-button> <el-button link type="primary" @click="handleMain(scope.row)" v-has-permi="['houseRental:houseRental:edit']">详情</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['houseRental:houseRental:query']">编辑</el-button> <el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['houseRental:houseRental:query']">编辑</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['houseRental:houseRental:remove']">删除</el-button> <el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['houseRental:houseRental:remove']">删除</el-button>
@@ -73,18 +89,76 @@
<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" />
</el-card> </el-card>
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="houseRentalFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="合同编号" prop="contractNo">
<el-input maxlength="20" show-word-limit v-model.trim="form.contractNo" placeholder="请输入合同编号" />
</el-form-item>
<el-form-item label="区域" prop="villageId">
<el-input disabled maxlength="20" v-model.trim="form.villageName" placeholder="请输入区域" />
</el-form-item>
<el-form-item label="承租方" prop="tenantName">
<el-input maxlength="20" show-word-limit v-model.trim="form.tenantName" placeholder="请输入承租方" />
</el-form-item>
<el-form-item label="电话" prop="tenantPhone">
<el-input maxlength="11" v-model.trim="form.tenantPhone" placeholder="请输入电话" />
</el-form-item>
<el-form-item label="房号" prop="houseId">
<el-input disabled maxlength="20" show-word-limit v-model.trim="form.houseName" placeholder="请输入房号" />
</el-form-item>
<el-form-item label="面积" prop="houseArea">
<el-input disabled maxlength="20" show-word-limit v-model.trim="form.houseArea" placeholder="请输入面积" />
</el-form-item>
<el-form-item label="收费模式" prop="chargeMode">
<el-input v-model.trim="form.chargeMode" placeholder="请输入收费模式" />
</el-form-item>
<el-form-item label="收费标准" prop="chargeStandard">
<el-input v-model.trim="form.chargeStandard" placeholder="请输入收费标准" />
</el-form-item>
<el-form-item label="合同状态" prop="contractStatus">
<el-input v-model.trim="form.contractStatus" placeholder="请输入合同状态" />
</el-form-item>
<el-form-item label="租赁起始日" prop="leaseStartDate">
<el-date-picker
style="width: 100%"
v-model.trim="form.leaseStartDate"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="date"
placeholder="请选择租赁起始日"
/>
</el-form-item>
<el-form-item label="租赁终止日" prop="leaseEndDate">
<el-date-picker
style="width: 100%"
v-model.trim="form.leaseEndDate"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="date"
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> </div>
</template> </template>
<script setup name="HouseRental" lang="ts"> <script setup name="HouseRental" lang="ts">
import Pageheader from '@/components/Pageheader/index.vue'; import Pageheader from '@/components/Pageheader/index.vue';
import { listHouseRental, delHouseRental } from '@/api/system/houseRental/index'; import { listHouseRental, delHouseRental, changeHouseRedientStatus, HouseRedientSign } from '@/api/system/houseRental/index';
import { HouseRentalVO, HouseRentalQuery, HouseRentalForm } from '@/api/system/houseRental/type'; import { HouseRentalVO, HouseRentalQuery, HouseRentalForm } from '@/api/system/houseRental/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
import { listHouseUseType_add_house } from '@/api/system/houseUse'; import { listHouseUseType_add_house } from '@/api/system/houseUse';
import { validator } from '@/utils/Reg';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_house_use } = toRefs<any>(proxy?.useDict('com_house_use'));
const { com_rental_status } = toRefs<any>(proxy?.useDict('com_rental_status')); const { com_rental_status } = toRefs<any>(proxy?.useDict('com_rental_status'));
const { com_rental_method } = toRefs<any>(proxy?.useDict('com_rental_method')); const { com_rental_method } = toRefs<any>(proxy?.useDict('com_rental_method'));
@@ -101,29 +175,34 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const houseRentalFormRef = ref<ElFormInstance>(); const houseRentalFormRef = ref<ElFormInstance>();
const villageInfo = localStorage.getItem('villageInfo');
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: HouseRentalForm = { type HouseRentalFormType = HouseRentalForm & {
id: undefined, villageName?: string;
rentalName: undefined, houseName?: string;
rent: undefined, houseArea?: number;
rentalType: undefined,
houseFace: undefined,
houseArea: undefined,
floorNo: undefined,
inTime: undefined,
facilities: undefined,
houseImageUrl: undefined,
supInfo: undefined,
phone: undefined,
vx: undefined,
email: undefined,
houseId: undefined
}; };
const data = reactive<PageData<HouseRentalForm, HouseRentalQuery>>({
const initFormData: HouseRentalFormType = {
'rentalId': undefined,
'contractNo': undefined,
'tenantName': undefined,
'tenantPhone': undefined,
'chargeMode': undefined,
'chargeStandard': undefined,
'contractStatus': undefined,
'leaseStartDate': undefined,
'leaseEndDate': undefined,
'houseId': undefined,
'villageId': undefined,
'remark': undefined
};
const data = reactive<PageData<HouseRentalFormType, HouseRentalQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
@@ -142,12 +221,44 @@ const data = reactive<PageData<HouseRentalForm, HouseRentalQuery>>({
phone: undefined, phone: undefined,
vx: undefined, vx: undefined,
email: undefined, email: undefined,
houseId: undefined, houseId: undefined
params: {} },
rules: {
contractNo: [
{ required: true, message: '请输入合同编号', trigger: 'blur' },
{ min: 1, max: 20, message: '合同编号长度必须在 1 到 20 个字符之间', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9_\-]+$/, message: '合同编号只能包含字母、数字,下划线和短横线', trigger: 'blur' }
],
tenantName: [
{ required: true, message: '请输入承租方姓名', trigger: 'blur' },
{ min: 1, max: 20, message: '承租方姓名长度必须在 1 到 20 个字符之间', trigger: 'blur' }
],
tenantPhone: [
{ required: true, message: '请输入承租方联系电话', trigger: 'blur' },
{ min: 11, max: 11, message: '承租方联系电话长度必须为 11 个字符', trigger: 'blur' },
{
validator: (rule: any, value: string, callback: any) => {
if (!validator(value, 'phone')) {
callback(new Error('承租方联系电话格式错误'));
} else {
callback();
}
},
trigger: ['blur', 'change']
}
],
chargeMode: [{ required: true, message: '请选择收费模式', trigger: 'change' }],
chargeStandard: [{ required: true, message: '请输入收费标准', trigger: 'blur' }],
contractStatus: [{ required: true, message: '请选择合同状态', trigger: 'change' }],
leaseStartDate: [{ required: true, message: '请选择租赁起始日', trigger: 'change' }],
leaseEndDate: [{ required: true, message: '请选择租赁终止日', trigger: 'change' }],
houseId: [{ required: true, message: '请选择房屋', trigger: 'change' }],
villageId: [{ required: true, message: '请选择小区', trigger: 'change' }],
houseArea: [{ required: true, message: '请输入房屋面积', trigger: 'blur' }]
} }
}); });
const { queryParams, form } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询房屋租赁管理列表 */ /** 查询房屋租赁管理列表 */
const getList = async () => { const getList = async () => {
@@ -203,7 +314,7 @@ const handleAdd = () => {
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: HouseRentalVO) => { const handleUpdate = (row?: HouseRentalVO) => {
const id = row?.id || ids.value[0]; const id = row?.id || ids.value[0];
router.push({ router.push({
path: '/house/edithouseRental', path: '/house/edithouseRental',
@@ -213,7 +324,7 @@ const handleUpdate = async (row?: HouseRentalVO) => {
}); });
}; };
/** 详情按钮操作 */ /** 详情按钮操作 */
const handleMain = async (row?: HouseRentalVO) => { const handleMain = (row?: HouseRentalVO) => {
router.push({ router.push({
path: '/house/houseMain', path: '/house/houseMain',
query: { query: {
@@ -241,6 +352,40 @@ function handleHouseUse(houseUser: string) {
const item = houseUseTypeList.value.find((item) => item.id === houseUser); const item = houseUseTypeList.value.find((item) => item.id === houseUser);
return item?.name || ''; return item?.name || '';
} }
// 签约
function sign(row: HouseRentalVO) {
form.value = {
...row.signingList?.[0]
};
const info = JSON.parse(villageInfo || '{}');
form.value.villageName = info.villageName;
form.value.houseName = row.name;
form.value.houseArea = row.houseArea;
form.value.houseId = row.houseId;
form.value.villageId = info.villageId;
form.value.rentalId = row.id;
dialog.visible = true;
dialog.title = '签约';
}
// 上架
function putOnSale(row: HouseRentalVO) {
changeHouseRedientStatus(row.id).then(() => {
proxy?.$modal.msgSuccess('操作成功');
getList();
});
}
function submitForm() {
houseRentalFormRef.value.validate((valid: boolean) => {
if (valid) {
HouseRedientSign(form.value).then(() => {
proxy?.$modal.msgSuccess('操作成功');
getList();
});
}
});
}
onMounted(() => { onMounted(() => {
getList(); getList();
getHouseUseType(); getHouseUseType();

View File

@@ -145,8 +145,36 @@ const form = ref({
const rules = ref({ const rules = ref({
areaId: [{ required: true, message: '请选择车位区域', trigger: 'blur' }], areaId: [{ required: true, message: '请选择车位区域', trigger: 'blur' }],
type: [{ required: true, message: '请选择车位类型', trigger: 'blur' }], type: [{ required: true, message: '请选择车位类型', trigger: 'blur' }],
parkingCode: [{ required: true, message: '请输入车位编号', trigger: 'blur' }], parkingCode: [
{ required: true, message: '请输入车位编号', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入车位编号'));
}
return validator(val, 'Character') ? callback() : callback(new Error('请输入中文,字母,数字,-,_等字符'));
}
}
],
residentId: [{ required: false, message: '请选择持有人', trigger: 'blur' }], residentId: [{ required: false, message: '请选择持有人', trigger: 'blur' }],
parkingFloor: [
{ required: false, message: '请输入车位楼层', trigger: 'blur' },
{
validator: (_: any, value: string | number, callback: (arg0?: Error) => void) => {
if (value === '' || value === null) {
return callback();
}
const val = Number(value);
if (Number.isNaN(val)) {
return callback(new Error('请输入数字'));
}
if (val <= 0) {
return callback(new Error('请输入大于0的数字'));
}
return callback();
}
}
],
contactPhone: [ contactPhone: [
{ required: true, message: '请输入联系人电话', trigger: 'blur' }, { required: true, message: '请输入联系人电话', trigger: 'blur' },
{ {

View File

@@ -14,11 +14,10 @@
<el-col :span="2"></el-col> <el-col :span="2"></el-col>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="住户姓名" prop="name"> <el-form-item label="住户姓名" prop="name">
<el-input maxlength="15" show-word-limit v-model.trim="form.name" placeholder="请输入住户姓名" /> <el-input maxlength="10" show-word-limit v-model.trim="form.name" placeholder="请输入住户姓名" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="住户性别" prop="gender"> <el-form-item label="住户性别" prop="gender">
@@ -51,7 +50,13 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row>
<el-col :span="11">
<el-form-item label="年龄" prop="age" v-if="form.idCardType !== '0'">
<el-input maxlength="3" v-model.number.trim="form.age" placeholder="年龄" />
</el-form-item>
</el-col>
</el-row>
<el-row> <el-row>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="与业主的关系" prop="ownerRelationship"> <el-form-item label="与业主的关系" prop="ownerRelationship">
@@ -152,12 +157,32 @@ const form = ref<ResidentForm>({
cardImageFront: '', cardImageFront: '',
idCardType: '0', // 证件类型 idCardType: '0', // 证件类型
cardImageBack: '', cardImageBack: '',
remark: '' //备注╬╬╬.╬╬╬╬╬ age: 0, //年龄
remark: '' //备注╬╬▓╬▐╬╬
}); });
const userHousePath = ref<CascaderValue>([]); const userHousePath = ref<CascaderValue>([]);
const rules = ref({ const rules = ref({
houseId: [{ required: true, message: '请选择房屋', trigger: 'blur' }], houseId: [{ required: true, message: '请选择房屋', trigger: 'blur' }],
name: [{ required: true, message: '请输入名称', trigger: 'blur' }], name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入名称'));
}
return validator(val, 'ChineseName') ? callback() : callback(new Error('请输入中文名称1-10位字符'));
}
}
],
age: [
{ required: true, message: '请输入年龄', trigger: 'blur' },
{
validator: (_: any, value: string | number, callback: (arg0?: Error) => void) => {
return validator(value, 'age') ? callback() : callback(new Error('请输入正确的年龄'));
},
trigger: 'blur'
}
],
gender: [{ required: true, message: '请选择性别', trigger: 'blur' }], gender: [{ required: true, message: '请选择性别', trigger: 'blur' }],
cardImageFront: [{ required: true, message: '请上传身份证照片', trigger: 'blur' }], cardImageFront: [{ required: true, message: '请上传身份证照片', trigger: 'blur' }],
cardImageBack: [{ required: true, message: '请上传身份证照片', trigger: 'blur' }], cardImageBack: [{ required: true, message: '请上传身份证照片', trigger: 'blur' }],
@@ -266,7 +291,7 @@ const handleBeforeUpload = (uploadFile: UploadRawFile) => {
} }
return true; return true;
}; };
// !== 提交 ============================================================================= // !==╬╬&╬╬╬ ╬ 提交 =============================================================================
// 提交 // 提交
const submitForm = () => { const submitForm = () => {

View File

@@ -4,7 +4,7 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" label-width="120" label-position="left" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" label-width="100" label-position="left" :model="queryParams" @submit.prevent="handleQuery" :inline="true">
<el-form-item label="与主业主关系" prop="ownerRelationship"> <el-form-item label="与主业主关系" prop="ownerRelationship">
<el-select v-model="queryParams.ownerRelationship" placeholder="请选择与主业主关系" style="width: 240px"> <el-select v-model="queryParams.ownerRelationship" placeholder="请选择与主业主关系" style="width: 240px">
<el-option v-for="item in com_resident_relationship" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in com_resident_relationship" :key="item.value" :label="item.label" :value="item.value" />
@@ -15,6 +15,12 @@
<el-option v-for="item in com_review" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in com_review" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="住户姓名" prop="name">
<el-input v-model.trim="queryParams.name" maxlength="10" placeholder="请输入住户姓名" />
</el-form-item>
<el-form-item label="房间号" prop="houseName">
<el-input v-model.trim="queryParams.houseName" maxlength="10" placeholder="请输入房间号" />
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button> <el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button> <el-button @click="resetQuery">重置</el-button>
@@ -170,10 +176,11 @@ const data = reactive<PageData<ResidentForm, ResidentQuery>>({
date: '', date: '',
ownerRelationship: null, ownerRelationship: null,
status: null, status: null,
houseId: null, houseId: null,
buildingId: null, buildingId: null,
unitNoId: null unitNoId: null,
houseName: null,
name: null
} }
}); });
@@ -199,7 +206,9 @@ const handleNodeClick = (data: Tree, node) => {
...{ ...{
houseId: null, houseId: null,
buildingId: null, buildingId: null,
unitNoId: null unitNoId: null,
houseName: null,
name: null
}, },
...obj2 ...obj2
}; };

View File

@@ -82,7 +82,7 @@ onUnmounted(() => {
}); });
function reset() { function reset() {
date.value = getLastNDays(10)[0]; date.value = getLastNDays(7)[0];
querySelect(); querySelect();
} }
@@ -372,10 +372,10 @@ const dayX = ref([]);
const inData = ref([]); const inData = ref([]);
const outData = ref([]); const outData = ref([]);
/** 近十天人员流入流出 - 平滑折线图 */ /** 近十天人员流入流出 - 平滑折线图╥╬ */
const flowLineOption = ref<EChartsOption>({ const flowLineOption = ref<EChartsOption>({
title: { title: {
text: '近日人员流动趋势', text: '近日人员流动趋势',
left: 0, left: 0,
top: 0, top: 0,
textStyle: { fontSize: 14, fontWeight: 'normal' } textStyle: { fontSize: 14, fontWeight: 'normal' }
@@ -461,7 +461,7 @@ const flowLineOption = ref<EChartsOption>({
function flowLineOptionFun(days: string[], InData: number[], OutData: number[]): EChartsOption { function flowLineOptionFun(days: string[], InData: number[], OutData: number[]): EChartsOption {
return { return {
title: { title: {
text: '近日人员流动趋势', text: '近日人员流动趋势',
left: 0, left: 0,
top: 0, top: 0,
textStyle: { fontSize: 14, fontWeight: 'normal' } textStyle: { fontSize: 14, fontWeight: 'normal' }
@@ -605,7 +605,7 @@ function MoveInUserType() {
{ value: res.owner, name: '业主', itemStyle: { color: '#0a74fe' } }, { value: res.owner, name: '业主', itemStyle: { color: '#0a74fe' } },
{ value: res.tenant, name: '租户', itemStyle: { color: '#fe3d4e' } }, { value: res.tenant, name: '租户', itemStyle: { color: '#fe3d4e' } },
{ value: res.relatives, name: '亲属', itemStyle: { color: '#00cf7f' } }, { value: res.relatives, name: '亲属', itemStyle: { color: '#00cf7f' } },
{ value: res.other, name: '其他', itemStyle: { color: '#f48e3b' } } { value: res.otherType, name: '其他', itemStyle: { color: '#f48e3b' } }
]; ];
}); });
} }

View File

@@ -31,7 +31,7 @@
<el-col :span="2"></el-col> <el-col :span="2"></el-col>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="建筑面积" prop="houseArea"> <el-form-item label="建筑面积" prop="houseArea">
<el-input maxlength="8" v-model="form.houseArea" placeholder="请输入建筑面积"> <el-input maxlength="8" v-model.trim="form.houseArea" placeholder="请输入建筑面积">
<template #append> <template #append>
<span></span> <span></span>
</template> </template>
@@ -42,7 +42,7 @@
<el-row> <el-row>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="公摊面积" prop="shareArea"> <el-form-item label="公摊面积" prop="shareArea">
<el-input maxlength="8" v-model="form.shareArea" placeholder="请输入公摊面积"> <el-input maxlength="8" v-model.trim="form.shareArea" placeholder="请输入公摊面积">
<template #append> <template #append>
<span></span> <span></span>
</template> </template>
@@ -52,7 +52,7 @@
<el-col :span="2"></el-col> <el-col :span="2"></el-col>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="实用面积" prop="internalArea"> <el-form-item label="实用面积" prop="internalArea">
<el-input maxlength="8" v-model="form.internalArea" placeholder="请输入实用面积"> <el-input maxlength="8" v-model.trim="form.internalArea" placeholder="请输入实用面积">
<template #append> <template #append>
<span></span> <span></span>
</template> </template>
@@ -61,7 +61,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input type="textarea" maxlength="200" show-word-limit :rows="5" v-model="form.remark" placeholder="请输入备注" /> <el-input type="textarea" maxlength="200" show-word-limit :rows="5" v-model.trim="form.remark" placeholder="请输入备注" />
</el-form-item> </el-form-item>
<el-form-item style="margin-top: 30px"> <el-form-item style="margin-top: 30px">
<el-button type="primary" @click="submitForm">提交</el-button> <el-button type="primary" @click="submitForm">提交</el-button>
@@ -80,6 +80,7 @@ import { getBuildinglists } from '@/api/system/house';
import { addStoreroom, getStoreroom, updateStoreroom } from '@/api/system/storeroom'; import { addStoreroom, getStoreroom, updateStoreroom } from '@/api/system/storeroom';
import { StoreroomVO } from '@/api/system/storeroom/types'; import { StoreroomVO } from '@/api/system/storeroom/types';
import { getByBUildingidtoUnitNo } from '@/api/system/houseUnit'; import { getByBUildingidtoUnitNo } from '@/api/system/houseUnit';
import { validator } from '@/utils/Reg';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@@ -100,7 +101,17 @@ const form = ref<StoreroomVO>({
const rules = ref({ const rules = ref({
buildingId: [{ required: true, message: '请选择楼栋', trigger: 'blur' }], buildingId: [{ required: true, message: '请选择楼栋', trigger: 'blur' }],
unitNoId: [{ required: true, message: '请选择单元', trigger: 'blur' }], unitNoId: [{ required: true, message: '请选择单元', trigger: 'blur' }],
storeroomNo: [{ required: true, message: '请输入储藏室号', trigger: 'blur' }], storeroomNo: [
{ required: true, message: '请输入储藏室号', trigger: 'blur' },
{
validator: (_: any, val: string | number, callback: (arg0?: Error) => void) => {
if (val === '' || val === null || val === undefined) {
return callback(new Error('请输入储藏室号'));
}
return validator(val, 'Character') ? callback() : callback(new Error('请输入中文,字母,数字,-,_等字符'));
}
}
],
houseArea: [ houseArea: [
{ required: true, message: '请输入建筑面积', trigger: 'blur' }, { required: true, message: '请输入建筑面积', trigger: 'blur' },
{ {
@@ -112,7 +123,7 @@ const rules = ref({
if (Number.isNaN(val)) { if (Number.isNaN(val)) {
return callback(new Error('请输入数字')); return callback(new Error('请输入数字'));
} }
if (val <= 0) { if (val < 0) {
return callback(new Error('请输入大于0的数字')); return callback(new Error('请输入大于0的数字'));
} }
return callback(); return callback();
@@ -130,7 +141,7 @@ const rules = ref({
if (Number.isNaN(val)) { if (Number.isNaN(val)) {
return callback(new Error('请输入数字')); return callback(new Error('请输入数字'));
} }
if (val <= 0) { if (val < 0) {
return callback(new Error('请输入大于0的数字')); return callback(new Error('请输入大于0的数字'));
} }
return callback(); return callback();
@@ -148,7 +159,7 @@ const rules = ref({
if (Number.isNaN(val)) { if (Number.isNaN(val)) {
return callback(new Error('请输入数字')); return callback(new Error('请输入数字'));
} }
if (val <= 0) { if (val < 0) {
return callback(new Error('请输入大于0的数字')); return callback(new Error('请输入大于0的数字'));
} }
return callback(); return callback();

View File

@@ -19,13 +19,13 @@
<div <div
class="topbox" class="topbox"
:class="{ :class="{
quickbg: Sideactive === 1, quickbg: sideActive === 1,
todobg: Sideactive === 2 todobg: sideActive === 2
}" }"
> >
<div <div
:class="{ :class="{
active: Sideactive === 1 active: sideActive === 1
}" }"
@click="handleSelect(1)" @click="handleSelect(1)"
> >
@@ -34,7 +34,7 @@
</div> </div>
<div <div
:class="{ :class="{
active: Sideactive === 2 active: sideActive === 2
}" }"
@click="handleSelect(2)" @click="handleSelect(2)"
> >
@@ -43,8 +43,8 @@
</div> </div>
</div> </div>
<div class="body flex-1"> <div class="body flex-1">
<quick v-if="Sideactive === SideList.quick"></quick> <quick v-if="sideActive === SideList.quick"></quick>
<todo v-else-if="Sideactive === SideList.todo"></todo> <todo v-else-if="sideActive === SideList.todo"></todo>
</div> </div>
</div> </div>
</div> </div>
@@ -68,7 +68,7 @@ import icon1 from './assets/user.png';
import icon2 from './assets/user.png'; import icon2 from './assets/user.png';
import icon3 from './assets/car.png'; import icon3 from './assets/car.png';
import icon4 from './assets/device.png'; import icon4 from './assets/device.png';
import icon5 from './assets/wuliao.png'; // import icon5 from './assets/wuliao.png';
import icon6 from './assets/tousu.png'; import icon6 from './assets/tousu.png';
import { getVillageByIdAPI } from '@/api/system/community'; import { getVillageByIdAPI } from '@/api/system/community';
@@ -81,7 +81,7 @@ import quick from './components/quick.vue';
import todo from './components/todo.vue'; import todo from './components/todo.vue';
import feeOverview from './components/feeOverview.vue'; import feeOverview from './components/feeOverview.vue';
import inandoutEcharts from './components/inandout.vue'; // import inandoutEcharts from './components/inandout.vue';
import repairechartsAll from './components/repairechartsAll.vue'; import repairechartsAll from './components/repairechartsAll.vue';
const houseStore = useHouseStore(); const houseStore = useHouseStore();
const villageinfo = ref({ const villageinfo = ref({
@@ -97,15 +97,15 @@ const list = ref([
// { name: '物料总数量', value: 458, icon: icon5 } // { name: '物料总数量', value: 458, icon: icon5 }
]); ]);
function getVillageinfo() { function getVillageInfo() {
const id = localStorage.getItem('villageid'); const id = localStorage.getItem('villageid');
workSpaceTotalListApi(id).then((res) => { workSpaceTotalListApi(id).then((res) => {
list.value[0].value = res.totalResident; list.value[0].value = res.totalResident;
list.value[1].value = res.totalHouse; list.value[1].value = res.totalHouse;
list.value[2].value = res.totalParking; list.value[2].value = res.totalParking;
list.value[3].value = res.totalDevice; list.value[3].value = res.totalDevice;
list.value[4].value = res.totalMaterial; list.value[4].value = res.totalComplaint;
list.value[5].value = res.totalComplaint; list.value[5].value = res.totalMaterial;
}); });
getVillageByIdAPI(id).then((res) => { getVillageByIdAPI(id).then((res) => {
villageinfo.value = { villageinfo.value = {
@@ -127,12 +127,12 @@ function getVillageinfo() {
houseStore.updateVilageinfo(res.data); houseStore.updateVilageinfo(res.data);
}); });
} }
getVillageinfo(); getVillageInfo();
// ! 侧边栏 // ! 侧边栏
const Sideactive = ref(1); const sideActive = ref(1);
function handleSelect(val: number) { function handleSelect(val: number) {
Sideactive.value = val; sideActive.value = val;
} }
// ! 侧边栏 // ! 侧边栏
/** 侧边栏功能枚举 */ /** 侧边栏功能枚举 */