8/1 限制前端输入字数

This commit is contained in:
Zy
2026-08-01 10:11:38 +08:00
parent 2907f9202c
commit ca5f214bac
33 changed files with 271 additions and 253 deletions

View File

@@ -9,9 +9,9 @@ VITE_APP_ENV='development'
# 开发环境
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'

View File

@@ -69,7 +69,6 @@ export const income_scourcApi = (datetime?: string): AxiosPromise<sourceType> =>
});
};
export interface PayMentMethod {
'wechatAmount': string;
'wechatPercent': string;
@@ -89,7 +88,6 @@ export const payment_methodApi = (datetime?: string): AxiosPromise<PayMentMethod
});
};
export interface Arrears_distrubution {
month: number;
amount: string;
@@ -133,17 +131,19 @@ export interface payment_trendType {
'unpaidAmount': string;
}
// 缴费走势分析
export const payment_trendApi = (startDate?: string, endDate?: string): AxiosPromise<{
items:payment_trendType[]
export const payment_trendApi = (
startDate?: string,
endDate?: string
): AxiosPromise<{
items: payment_trendType[];
}> => {
return request({
url: '/api/analysis/income/payment_trend',
method: 'get',
params: {
startDate,
endDate,
endDate
}
});
};

View File

@@ -93,8 +93,6 @@ export const get_movein_moveout_list = (query?: {
});
};
export const get_person_age = (query?: {
dateTime: string;
}): AxiosPromise<

View File

@@ -3,7 +3,7 @@ export interface BillVO {
* 账单管理表id
*/
id: string;
billName:string;
billName: string;
/**
* 账单编号

View File

@@ -15,7 +15,7 @@ export interface ChargeItemVO {
itemName: string;
/**
* 计费方式 0单次收费 1周期收费 2仪表收费
* 计费方式 0单次收费 1 周期收费 2 仪表收费
*/
billingMethod: string;
@@ -53,6 +53,10 @@ export interface ChargeItemVO {
* 小区id
*/
villageId: string;
/**
* 状态 0正常 1停用
*/
status: string;
}
export interface ChargeItemForm extends BaseEntity {

View File

@@ -88,7 +88,6 @@ export interface WarehouseQuery extends PageQuery {
*/
warehouseName?: string;
/**
* 联系人*
*/

View File

@@ -82,9 +82,7 @@ export interface AppUserList {
/**
* 获取用户端app-用户
* */
export const getAppUser = (
data
): AxiosPromise<AppUserList[]> => {
export const getAppUser = (data): AxiosPromise<AppUserList[]> => {
return request({
url: '/car/getAppUserList',
method: 'GET',

View File

@@ -19,7 +19,7 @@ export interface DeviceVO {
* 设备编号
*/
deviceCode: string;
shelfLife:string;
shelfLife: string;
/**
* 设备型号

View File

@@ -150,7 +150,6 @@ export interface VersionQuery extends PageQuery {
*/
downloadUrl?: string;
/**
* 0-禁用 1-启用
*/

View File

@@ -259,4 +259,3 @@ aside {
.el-button {
border-radius: 5px;
}

View File

@@ -85,7 +85,7 @@ const fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(
watch(
() => props.modelValue,
async (val:string) => {
async (val: string) => {
if (val) {
let temp = 1;
// 首先将值转为数组

View File

@@ -80,11 +80,7 @@ export function buildHousePathCache(treeData: Tree[]) {
pathCache.set('label', new Map());
pathCache.set('value', new Map());
const stack: [Tree, (string | number)[], (string | number)[]][] = treeData.map(node => [
node,
[node.label],
[node.value]
]);
const stack: [Tree, (string | number)[], (string | number)[]][] = treeData.map((node) => [node, [node.label], [node.value]]);
while (stack.length) {
const [node, labelPath, valPath] = stack.pop()!;
@@ -99,11 +95,7 @@ export function buildHousePathCache(treeData: Tree[]) {
if (node.children?.length) {
const reverseChildren = [...node.children].reverse();
for (const child of reverseChildren) {
stack.push([
child,
[...labelPath, child.label],
[...valPath, child.value]
]);
stack.push([child, [...labelPath, child.label], [...valPath, child.value]]);
}
}
}

View File

@@ -190,7 +190,6 @@ export function neqMoney(a: string | number, b: string | number): boolean {
return !toBig(a).eq(toBig(b));
}
// 保留2位小数金额专用
export function toFixed2(val) {
return Number(val).toFixed(2);

View File

@@ -6,7 +6,7 @@
<el-card shadow="hover">
<el-form ref="queryFormRef" label-width="90px" label-position="left" :model="queryParams" :inline="true">
<el-form-item label="投诉类型" prop="typeId">
<el-select v-model="queryParams.typeId" placeholder="请选择投诉类型" style="width: 240px">
<el-select @clear="handleClear" clearable v-model="queryParams.typeId" placeholder="请选择投诉类型" style="width: 240px">
<el-option :disabled="item.status === '1'" v-for="item in complainTypeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
@@ -120,6 +120,11 @@ const handleQuery = () => {
getList();
};
/** 清空投诉类型 */
const handleClear = () => {
queryParams.value.typeId = undefined;
handleQuery();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();

View File

@@ -7,7 +7,7 @@
<div class="title">基本信息</div>
</template>
<div class="addBuildingFormBody">
<div class="formbox">
<div class="formBox">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-row>
<el-col :span="12">
@@ -37,14 +37,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="装修押金(元)" prop="decorationDeposit">
<el-input
type="number"
min="0"
step="0.01"
maxlength="8"
placeholder="请输入装修押金最多保留2位小数"
v-model.trim.number="form.decorationDeposit"
/>
<el-input maxlength="8" placeholder="请输入装修押金最多保留2位小数" v-model.number.trim="form.decorationDeposit" />
</el-form-item>
</el-col>
</el-row>
@@ -64,6 +57,7 @@ import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { addDecoration, getDecoration, updateDecoration } from '@/api/system/Decoration';
import { useHouseStore } from '@/store/modules/house';
import { validator } from '@/utils/Reg';
const houseStore = useHouseStore();
const treedata = ref([]);
@@ -77,39 +71,12 @@ const form = ref({
'buildingId': null,
'unitNoId': null,
'applyName': '',
'inspectionResult': '0',
'decorationCompany': '',
'decorationContent': '',
'decorationDeposit': null
});
/**
* 通用金额校验器
* @param value 输入值
* @param type money金额 / num整数
* @returns 校验提示信息,空则校验通过
*/
const validator = (value: number | null, type: 'money' | 'num') => {
// 空值由 required 规则拦截,这里只处理有值的情况
if (value === null || value === undefined) return '';
// 转字符串处理小数位数
const strVal = String(value);
// 禁止负数
if (value < 0) return '金额不能为负数';
if (type === 'money') {
// 匹配最多两位小数
const reg = /^\d+(\.\d{1,2})?$/;
if (!reg.test(strVal)) return '金额仅支持最多两位小数';
// 限制金额上限(可按需修改)
if (value > 9999999.99) return '押金金额不能超过9999999.99元';
} else if (type === 'num') {
const reg = /^\d+$/;
if (!reg.test(strVal)) return '只能输入正整数';
}
return '';
};
const rules = {
houseId: [{ required: true, message: '请选择具体房屋', trigger: 'blur' }],
applyName: [{ required: true, message: '请输入申请人', trigger: 'blur' }],
@@ -120,9 +87,18 @@ const rules = {
if (val === '' || val === null) {
return callback(new Error('请输入装修押金'));
}
const msg = validator(val, 'money');
if (msg) callback(new Error(msg));
else callback();
const num = Number(val);
console.log(val);
if (Number.isNaN(num)) {
return callback(new Error('请输入装修押金'));
}
if (num < 0) {
return callback(new Error('装修押金不能为负数'));
}
if (num > 99999999) {
return callback(new Error('装修押金过大'));
}
return validator(val, 'money');
},
trigger: ['blur', 'change']
}
@@ -184,9 +160,7 @@ const submitForm = () => {
// 返回
function handleBack() {
router.push({
path: '/repair/decoration'
});
router.back();
}
</script>
@@ -198,7 +172,7 @@ function handleBack() {
margin-top: 20px;
flex: 1;
.formbox {
.formBox {
width: 1000px;
margin: 0 auto;
}

View File

@@ -9,7 +9,7 @@
<el-input maxlength="20" v-model="queryParams.applyName" placeholder="请输入申请人姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="装修验收结果" prop="inspectionResult">
<el-select v-model="queryParams.inspectionResult" placeholder="请选择装修验收结果" style="width: 240px">
<el-select clearable @clear="handleClear" v-model="queryParams.inspectionResult" placeholder="请选择装修验收结果" style="width: 240px">
<el-option v-for="item in com_decoration_status" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
@@ -59,11 +59,16 @@
link
type="primary"
v-if="scope.row.inspectionResult !== '1'"
@click="handleInspection(scope.row, 0, '验收')"
@click="handleInspection(scope.row, EditType.Add, '验收')"
v-has-permi="['repair:edit:decoration']"
>验收</el-button
>
<el-button link type="primary" v-else @click="handleInspection(scope.row, 1, '详情')" v-has-permi="['decoration:decoration:query']"
<el-button
link
type="primary"
v-else
@click="handleInspection(scope.row, EditType.Detail, '详情')"
v-has-permi="['decoration:decoration:query']"
>详情</el-button
>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['decoration:decoration:edit']">编辑</el-button>
@@ -77,42 +82,47 @@
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="decorationFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="小区名称" prop="villageName" v-if="isedit === 1">
<el-input :disabled="isedit === 1" v-model="form.villageName" />
<div v-if="isEditComputed">
<el-form-item label="小区名称" prop="villageName">
<el-input :disabled="isEditComputed" v-model="form.villageName" />
</el-form-item>
<el-form-item label="房屋号" prop="houseName" v-if="isedit === 1">
<el-input :disabled="isedit === 1" v-model="form.houseName" />
<el-form-item label="房屋号" prop="houseName">
<el-input :disabled="isEditComputed" v-model="form.houseName" />
</el-form-item>
<el-form-item label="申请人" prop="applyName" v-if="isedit === 1">
<el-input :disabled="isedit === 1" v-model="form.applyName" />
<el-form-item label="申请人" prop="applyName">
<el-input :disabled="isEditComputed" v-model="form.applyName" />
</el-form-item>
<el-form-item label="装修公司" prop="decorationCompany" v-if="isedit === 1">
<el-input :disabled="isedit === 1" v-model="form.decorationCompany" />
<el-form-item label="装修公司" prop="decorationCompany">
<el-input :disabled="isEditComputed" v-model="form.decorationCompany" />
</el-form-item>
<el-form-item label="装修内容" prop="decorationContent" v-if="isedit === 1">
<el-input :disabled="isedit === 1" v-model="form.decorationContent" />
<el-form-item label="装修内容" prop="decorationContent">
<el-input :disabled="isEditComputed" v-model="form.decorationContent" />
</el-form-item>
<el-form-item label="装修押金" prop="decorationDeposit" v-if="isedit === 1">
<el-input :disabled="isedit === 1" v-model="form.decorationDeposit" />
<el-form-item label="装修押金" prop="decorationDeposit">
<el-input :disabled="isEditComputed" v-model="form.decorationDeposit" />
</el-form-item>
<el-form-item label="验收结果" prop="inspectionResult">
<el-select :disabled="isedit === 1" v-model="form.inspectionResult" placeholder="请选择装修验收结果">
<el-option
v-for="item in com_decoration_status.filter((item) => item.value !== '0')"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</div>
<el-form-item label="验收结果" :prop="isEditComputed ? '' : 'inspectionResult'">
<el-select :disabled="isEditComputed" v-model="form.inspectionResult" placeholder="请选择装修验收结果">
<el-option v-for="item in com_decoration_status" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="验收人" prop="inspectionName">
<el-input :disabled="isedit === 1" v-model="form.inspectionName" />
<el-form-item label="验收人" :prop="isEditComputed ? '' : 'inspectionName'">
<el-input :disabled="isEditComputed" v-model="form.inspectionName" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input :disabled="isedit === 1" v-model="form.remark" type="textarea" placeholder="请输入" :rows="5"></el-input>
<el-input
:disabled="isEditComputed"
v-model="form.remark"
type="textarea"
maxlength="200"
show-word-limit
:placeholder="isEditComputed ? '' : '请输入'"
:rows="5"
></el-input>
</el-form-item>
</el-form>
<template #footer v-if="isedit !== 1">
<template #footer v-if="!isEditComputed">
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
@@ -123,7 +133,7 @@
</template>
<script setup name="Decoration" lang="ts">
import { listDecoration, delDecoration, updateDecoration } from '@/api/system/Decoration/index';
import { listDecoration, delDecoration, updateDecoration } from '@/api/system/Decoration';
import { DecorationVO, DecorationQuery, DecorationForm } from '@/api/system/Decoration/type';
import { checkPermi } from '@/utils/permission';
@@ -208,6 +218,11 @@ const handleQuery = () => {
getList();
};
/** 清空按钮操作 */
const handleClear = () => {
queryParams.value.inspectionResult = undefined;
handleQuery();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
@@ -230,7 +245,7 @@ const handleAdd = () => {
};
/** 修改按钮操作 */
const handleUpdate = async (row?: DecorationVO) => {
const handleUpdate = (row?: DecorationVO) => {
const _id = row?.id || ids.value[0];
router.push({
path: '/repair/editDecoration',
@@ -240,16 +255,24 @@ const handleUpdate = async (row?: DecorationVO) => {
});
};
const isedit = ref(1);
enum EditType {
/** 验收 */
Add = 1,
/** 详情 */
Detail = 2
}
const isEdit = ref(EditType.Add);
const isEditComputed = computed(() => isEdit.value !== EditType.Add);
/** 验收 */
const handleInspection = (row: DecorationVO, edit: number, type: string) => {
const handleInspection = (row: DecorationVO, edit: EditType, type: string) => {
form.value = {
...form.value,
...row
};
dialog.visible = true;
dialog.title = type;
isedit.value = edit;
isEdit.value = edit;
};
/** 提交按钮 */

View File

@@ -73,17 +73,21 @@
ref="uploadRef"
:show-file-list="true"
name="files"
:on-exceed="handleExceed"
:headers="headerToken()"
:on-remove="handleAvatarRemove"
:on-change="handleProgress"
:before-upload="beforeAvatarUpload"
accept=".doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif,.bmp,.webp"
accept=".doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif,.bmp,.webp,.zip"
>
<div class="uploadBtn flex flex-items-center">
<el-icon><Upload /></el-icon>
<span>上传文件</span>
</div>
<template #tip>仅支持doc/docx/xls/xlsx/jpg/png/gif等</template>
<template #tip>
<div>仅支持doc/docx/xls/xlsx/jpg/png/gif/bmp/webp/zip等格式</div>
<span style="font-size: smaller">最多上传 {{ MAX_FILE_COUNT }} 个文件</span>
</template>
</el-upload>
</el-form-item>
</el-col>
@@ -108,7 +112,7 @@ import { addHandleLog, getHandleLog, updateHandleLog, uploadAttachments } from '
import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus';
import { useUserStore } from '@/store/modules/user';
import { ElMessage } from 'element-plus';
import { Upload } from '@element-plus/icons-vue'; // 确保引入图标
import { Upload } from '@element-plus/icons-vue'; // 确保引入图标组件╬╬╬
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/handleLog/uploadAttachments';
const route = useRoute();
@@ -152,33 +156,29 @@ const handleAvatarRemove: UploadProps['onRemove'] = (uploadFile: UploadUserFile,
};
// 定义大小限制常量 (单位: MB)
const MAX_SINGLE_FILE_SIZE = 10 * 1024 * 1024; // 10MB in bytes
const MAX_TOTAL_FILE_SIZE = 20 * 1024 * 1024; // 20MB in bytes
const MAX_SINGLE_FILE_SIZE = 10 * 1024 * 1024; // 10MB
const MAX_TOTAL_FILE_SIZE = 20 * 1024 * 1024; // 20MB
const MAX_FILE_COUNT = 9; // 最大上传文件数量
const uploadRef = ref();
// 添加一个锁,防止 on-change 递归调用导致重复提示
const isChecking = ref(false);
const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
ElMessage.warning(`最多只能上传 ${MAX_FILE_COUNT} 个文件,当前已上传 ${uploadFiles.length} 个文件`);
};
const handleProgress: UploadProps['onChange'] = (uploadFile: UploadUserFile, uploadFiles: UploadFiles) => {
// 如果正在校验中,直接返回,避免重复执行
// 先校验当前触发的文件类型
if (!beforeAvatarUpload(uploadFile)) {
uploadRef.value.handleRemove(uploadFile);
uploadRef.value?.handleRemove(uploadFile);
return;
}
if (isChecking.value) return;
// 加锁
isChecking.value = true;
// 创建一个新数组来存储校验通过的文件
const validFiles: CustomUploadFile[] = [];
let newFilesTotalSize = 0;
let hasError = false;
let errorMsg = '';
// 1. 遍历所有文件,筛选出符合单文件大小要求的文件,并计算新文件总大小
// 遍历所有文件,校验新文件
for (const file of uploadFiles) {
// 只校验新上传的文件 (有 raw 属性)
if (file.raw) {
// 检查单个文件大小
if (file.raw.size > MAX_SINGLE_FILE_SIZE) {
// 如果发现单个文件超标,标记错误,并不将该文件加入 validFiles
if (!hasError) {
errorMsg = `文件 ${file.name} 大小超过 10MB已自动移除`;
hasError = true;
@@ -187,57 +187,37 @@ const handleProgress: UploadProps['onChange'] = (uploadFile: UploadUserFile, upl
}
newFilesTotalSize += file.raw.size;
}
// 将非超标文件(包括旧文件和未超标的新文件)暂时加入
console.log(file);
validFiles.push({
...file,
ossId: (file as CustomUploadFile).ossId ?? null
});
}
// 2. 如果存在单文件超标的情况,先处理这部分逻辑
if (hasError) {
ElMessage.error(errorMsg);
// 更新 fileList 为移除了超标文件的列表
fileList.value = validFiles;
return;
}
// 3. 检查新文件总大小是否超过 20MB
if (newFilesTotalSize > MAX_TOTAL_FILE_SIZE) {
ElMessage.error(`所有新上传文件的总大小不能超过 20MB当前已选新文件总大小约为 ${(newFilesTotalSize / 1024 / 1024).toFixed(2)}MB`);
// 策略:移除最后添加的那个导致超标的文件
// 注意这里需要找到最后添加的那个文件。uploadFile 参数就是当前触发的文件
// 但如果是一次性选择多个文件uploadFile 可能是其中一个。
// 更稳妥的方式是:如果总和超标,则移除所有新文件中最后加入的那个,或者提示用户手动减少。
// 这里我们尝试移除最后加入的一个新文件
// 找出所有新文件
const newFiles = validFiles.filter((f) => f.raw);
if (newFiles.length > 0) {
// 移除最后一个新文件
const lastNewFile = newFiles[newFiles.length - 1];
const filteredFiles = validFiles.filter((f) => f.uid !== lastNewFile.uid);
fileList.value = filteredFiles;
} else {
// 理论上不会走到这里,因为如果没有新文件,总大小不会增加
fileList.value = validFiles;
}
return;
}
// 4. 校验全部通过,更新 fileList
// 只有当 validFiles 与当前 fileList 不同时才赋值,避免不必要的触发
if (validFiles.length !== fileList.value.length || validFiles.some((f, i) => f.uid !== fileList.value[i]?.uid)) {
// 只有文件列表真正变化时才更新,避免不必要的响应式触发
const currentList = fileList.value;
if (validFiles.length !== currentList.length || validFiles.some((f, i) => f.uid !== currentList[i]?.uid)) {
fileList.value = validFiles;
}
// 解锁,允许下一次变更触发校验
// 使用 nextTick 或 setTimeout 确保 DOM 更新后再解锁,防止极快连续操作的问题
setTimeout(() => {
isChecking.value = false;
}, 100);
};
const blockedExts = ['.exe', '.bat', '.cmd', '.msi'];
@@ -259,6 +239,8 @@ const allowExts = [
'.gif',
'.bmp',
'.webp',
// archive
'.zip',
// 专业/矢量图
'.psd',
'.ai',
@@ -389,7 +371,7 @@ function handleBack() {
width: 100%;
}
&:deep(.el-upload-list) {
max-height: 200px;
max-height: 250px;
overflow-y: auto;
overflow-x: hidden;
}

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex flex-col h-full p-2">
<pageheader title="办事记录"></pageheader>
<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">
@@ -37,7 +37,7 @@
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="办事内容" align="center" prop="content">
<template #default="scope">
<div class="color-blue" v-html="scope.row.content"></div>
<div class="color-blue moreRowEllipsis" v-html="scope.row.content"></div>
</template>
</el-table-column>
<el-table-column label="求助人" align="center" prop="seekName" width="120" />
@@ -50,7 +50,7 @@
</el-table-column>
<el-table-column label="附属材料" width="100" align="center" prop="handlePosition">
<template #default="scope">
<el-button type="primary" @click="openDiage(scope.row)" link>点击查看</el-button>
<el-button type="primary" v-if="scope.row.handleLogFileList?.length > 0" @click="openDialog(scope.row)" link>点击查看</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right" class-name="small-padding fixed-width">
@@ -64,15 +64,15 @@
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改办事记录对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<ul class="ulbox" v-if="dialog.files.length > 0">
<ul class="ulBox" v-if="dialog.files.length > 0">
<li @click="handleDown(item)" class="files_item" v-for="(item, index) in dialog.files" :key="index">
<div>
{{ item.fileName }}
</div>
<span class="clickdown">点击下载</span>
<span class="clickDown">点击下载</span>
</li>
</ul>
<ul class="ulbox" v-else>
<ul class="ulBox" v-else>
<li class="files_item_no">
<div>暂无数据</div>
</li>
@@ -83,14 +83,13 @@
</template>
<script setup name="HandleLog" lang="ts">
import { listHandleLog, delHandleLog } from '@/api/system/HandleLog/index';
import { HandleLogVO, HandleLogQuery, HandleLogForm } from '@/api/system/HandleLog/type';
import { listHandleLog, delHandleLog } from '@/api/system/HandleLog';
import { HandleLogVO, HandleLogQuery, HandleLogForm, FileVO } from '@/api/system/HandleLog/type';
import { checkPermi } from '@/utils/permission';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const handleLogList = ref<HandleLogVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
@@ -99,7 +98,6 @@ const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const handleLogFormRef = ref<ElFormInstance>();
const initFormData: HandleLogForm = {
id: undefined,
@@ -126,7 +124,7 @@ const data = reactive<PageData<HandleLogForm, HandleLogQuery>>({
}
});
const { queryParams, form, rules } = toRefs(data);
const { queryParams } = toRefs(data);
/** 查询办事记录列表 */
const getList = async () => {
@@ -169,7 +167,7 @@ const handleAdd = () => {
};
/** 修改按钮操作 */
const handleUpdate = async (row?: HandleLogVO) => {
const handleUpdate = (row?: HandleLogVO) => {
const _id = row ? row.id : ids.value[0];
router.push({
path: '/repair/editHandleLog',
@@ -192,14 +190,14 @@ const dialog = reactive({
title: '',
files: []
});
const openDiage = (row) => {
// 查看附属材料
const openDialog = (row: HandleLogVO) => {
dialog.visible = true;
dialog.title = '查看附属材料';
console.log(row);
dialog.files = row.handleLogFileList ?? [];
};
const handleDown = (item) => {
// 下载文件
const handleDown = (item: FileVO) => {
proxy?.$download.oss(item.ossId);
};
onMounted(() => {
@@ -210,8 +208,16 @@ onMounted(() => {
<style lang="scss" scoped>
.el-table {
height: calc(100% - 80px);
// 超出部分省略号 3 行
.moreRowEllipsis {
word-break: break-all;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
}
.ulbox {
.ulBox {
max-height: 300px;
overflow-y: auto;
overflow-x: hidden;
@@ -233,12 +239,12 @@ onMounted(() => {
&:hover {
background-color: #f5f5f5;
color: #409eff;
.clickdown {
.clickDown {
color: #409eff;
}
}
.clickdown {
.clickDown {
color: #cccc;
font-size: small;
}

View File

@@ -12,7 +12,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="公告标题" prop="noticeTitle">
<el-input maxlength="30" v-model.trim="form.noticeTitle" placeholder="请输入公告标题" />
<el-input maxlength="50" v-model.trim="form.noticeTitle" placeholder="请输入公告标题" />
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->

View File

@@ -16,7 +16,7 @@
</el-radio-group>
<Holder v-if="form.scope === '1'" returnvalue="id" ref="HolderRef" :isMultiple="true" :userid="checkoutUses" @change="handleSelect">
<template #default>
<el-button type="primary" link @click="OpenUseTablesfun">选择业主</el-button>
<el-button type="primary" link @click="OpenUseTablesFun">选择业主</el-button>
</template>
</Holder>
</div>
@@ -26,9 +26,9 @@
<el-row>
<el-col :span="8">
<el-form-item label="时间范围" prop="timedata">
<el-form-item label="时间范围" prop="timeData">
<el-date-picker
v-model="timedata"
v-model="timeData"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
@@ -73,18 +73,16 @@ const { com_questionnaire_scope } = toRefs<any>(proxy?.useDict('com_questionnair
const router = useRouter();
const route = useRoute();
import { listResident } from '@/api/system/residentReviewProcess/resident';
const form = ref({
scope: '0',
residents: '',
remark: ''
});
const timedata = ref([]);
const timeData = ref([]);
// ! 路由参数 --------------------------------------------------------------------------------
// 打开
const HolderRef = ref<InstanceType<typeof Holder>>();
function OpenUseTablesfun() {
function OpenUseTablesFun() {
HolderRef.value.open(form.value.residents.split(',').filter((item) => item));
}
// 多选
@@ -92,8 +90,8 @@ const checkoutUses = ref([]);
function handleSelect(val) {
checkoutUses.value = val;
form.value.residents = val
.map((item) => item)
.filter((item) => item)
.map((item: string) => item)
.filter((item: string) => item)
.join(',');
}
@@ -104,25 +102,21 @@ const editData = ref<QuestionComponentnaireForm>(null);
// ! 路由参数 --------------------------------------------------------------------------------
const userid = ref(null);
if (route.query.id) {
onMounted(() => {
if (route.query.id) {
userid.value = route.query.id;
handleEdit(userid.value);
} else {
} else {
handleAdd();
}
//
const tableData = ref([]);
}
});
// 新增问卷
function handleAdd() {
editData.value = null;
questionnaireEditRef.value?.reset();
}
// 编辑问卷
async function handleEdit(id: string | number) {
const res = await listResident();
tableData.value = res.rows;
function handleEdit(id: string | number) {
// 父组件自己调用接口获取数据
getQuestionnaire(id).then((res) => {
editData.value = res.data as QuestionComponentnaireForm;
@@ -137,7 +131,7 @@ async function handleEdit(id: string | number) {
.split(',')
.filter((item) => item)
.map((item) => item);
timedata.value = [res.data.startTime, res.data.endTime];
timeData.value = [res.data.startTime, res.data.endTime];
});
}
@@ -145,13 +139,13 @@ async function handleEdit(id: string | number) {
async function handleSave(data: QuestionComponentnaireForm) {
try {
// 父组件自己调用接口提交
if (userid.value) {
if (userid.value !== null) {
await updateQuestionnaire({
...data,
content: JSON.stringify(data.content),
status: '0', // 草稿状态
startTime: timedata.value[0],
endTime: timedata.value[1],
startTime: timeData.value[0],
endTime: timeData.value[1],
...form.value
});
} else {
@@ -159,8 +153,8 @@ async function handleSave(data: QuestionComponentnaireForm) {
...data,
content: JSON.stringify(data.content),
status: '0', // 草稿状态
startTime: timedata.value[0],
endTime: timedata.value[1],
startTime: timeData.value[0],
endTime: timeData.value[1],
...form.value
});
}
@@ -175,13 +169,13 @@ async function handleSave(data: QuestionComponentnaireForm) {
// 发布问卷
async function handlePublish(data: QuestionComponentnaireForm) {
try {
if (userid.value) {
if (userid.value !== null) {
await updateQuestionnaire({
...data,
content: JSON.stringify(data.content),
status: '1', // 发布状态
startTime: timedata.value[0],
endTime: timedata.value[1],
startTime: timeData.value[0],
endTime: timeData.value[1],
...form.value
});
} else {
@@ -189,8 +183,8 @@ async function handlePublish(data: QuestionComponentnaireForm) {
...data,
content: JSON.stringify(data.content),
status: '1', // 发布状态
endTime: timedata.value[1],
startTime: timedata.value[0],
endTime: timeData.value[1],
startTime: timeData.value[0],
...form.value
});
}
@@ -224,7 +218,7 @@ function handleCancel() {
position: relative;
.formBody {
.checkoutSideUsesbox {
.checkoutSideUsesBox {
margin-left: 130px;
display: grid;
grid-template-columns: repeat(5, 1fr);

View File

@@ -36,8 +36,8 @@
</template>
<div class="question-title-area">
<el-input maxlength="20" show-word-limit v-model="form.title" placeholder="请输入问卷标题" class="title-input" />
<el-input maxlength="200" show-word-limit v-model="form.description" type="textarea" placeholder="请输入问卷描述(选填)" />
<el-input maxlength="20" show-word-limit v-model.trim="form.title" placeholder="请输入问卷标题" class="title-input" />
<el-input maxlength="200" show-word-limit v-model.trim="form.description" type="textarea" placeholder="请输入问卷描述(选填)" />
</div>
<draggable v-model="form.content" group="components" item-key="cid" class="question-list" @add="handleAddQuestion">
@@ -72,16 +72,21 @@
</template>
<el-form style="position: sticky; top: 60px" label-width="80px">
<el-form-item label="题目标题">
<el-input v-model="activeQuestion.title" placeholder="请输入题目标题" />
<el-input maxlength="20" v-model.trim="activeQuestion.title" placeholder="请输入题目标题" />
</el-form-item>
<el-form-item label="是否必填">
<el-switch v-model="activeQuestion.required" />
<el-switch v-model.trim="activeQuestion.required" />
</el-form-item>
<!-- 单选/多选特有属性 -->
<template v-if="activeQuestion.type === 'radio' || activeQuestion.type === 'checkbox'">
<el-divider content-position="left">选项设置</el-divider>
<div v-for="(opt, idx) in activeQuestion.options" :key="idx" class="option-item">
<el-input v-model="opt.label" placeholder="选项内容" style="width: 200px; margin-right: 10px; margin-bottom: 10px" />
<el-input
maxlength="20"
v-model.trim="opt.label"
placeholder="选项内容"
style="width: 200px; margin-right: 10px; margin-bottom: 10px"
/>
<el-button link type="danger" :icon="Delete" @click="activeQuestion.options.splice(idx, 1)"> 删除 </el-button>
</div>
<el-button type="primary" @click="activeQuestion.options.push({ label: '新选项' })"> 添加选项 </el-button>

View File

@@ -82,7 +82,8 @@
v-model:file-list="fileList"
:action="uploadUrl"
list-type="picture-card"
:limit="1"
:limit="9"
:on-exceed="handleExceed"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:before-upload="handleChangeFile"
@@ -213,6 +214,9 @@ const handleChangeFile: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) =
return true;
};
const handleExceed: UploadProps['onExceed'] = () => {
ElMessage.warning(`最多只能上传 9 个文件`);
};
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
dialogImageUrl.value = uploadFile.url!;
dialogVisible.value = true;

View File

@@ -10,7 +10,13 @@
</el-form-item>
<el-form-item label="收费类型" prop="typeId">
<el-select v-model="form.typeId">
<el-option v-for="(item, index) in chargeitemlist" :key="index" :value="item.id" :label="item.name"></el-option>
<el-option
:disabled="item.status === '1'"
v-for="(item, index) in chargeItemList"
:key="index"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="计费方式" prop="billingType">
@@ -84,7 +90,6 @@ const { com_charge_item_method } = toRefs<any>(proxy?.useDict('com_charge_item_m
const router = useRouter();
const route = useRoute();
const formRef = ref();
const form = ref({
'id': null,
@@ -107,7 +112,7 @@ const rules = ref({
{ required: true, message: '请输入 费用单价', trigger: 'blur' },
{
validator: (_: any, value: string | number, callback: (arg0?: Error) => any) => {
if (value === '' || value === null) {
if (value === '' || value === null || value === undefined || value === 0) {
return callback(new Error('请输入费用单价'));
} else {
const val = Number(value);
@@ -128,9 +133,9 @@ const rules = ref({
carryMethod: [{ required: true, message: '请选择 进位方式', trigger: 'blur' }]
});
const userid = ref(null);
const chargeitemlist = ref([]);
const chargeItemList = ref([]);
listChargeType().then((res) => {
chargeitemlist.value = res.rows;
chargeItemList.value = res.rows;
});
if (route.query.id) {
userid.value = route.query.id;
@@ -151,12 +156,12 @@ const submitForm = () => {
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (userid.value) {
updateChargeItem(form.value).then((res) => {
updateChargeItem(form.value).then(() => {
ElMessage.success('编辑成功');
cancelForm();
});
} else {
addChargeItem(form.value).then((res) => {
addChargeItem(form.value).then(() => {
ElMessage.success('添加成功');
cancelForm();
});
@@ -170,9 +175,9 @@ const cancelForm = () => {
};
// 车位费 物业费
const typeNamelist = ref(['2049026520314445826']);
const typeNameList = ref(['2049026520314445826']);
function placeHolderFun(typeId: string) {
return typeNamelist.value.includes(typeId) ? '请输入每日费用单价' : '请输入费用单价';
return typeNameList.value.includes(typeId) ? '请输入每日费用单价' : '请输入费用单价';
}
</script>

View File

@@ -31,7 +31,13 @@
<div>
<el-form-item label="收费项目" prop="itemId">
<el-select v-model="queryParams.chargeItemId" placeholder="请选择收费项目">
<el-option v-for="(item, index) in chargeItems" :key="index" :value="item.id" :label="item.itemName"></el-option>
<el-option
:disabled="item.status === '1'"
v-for="(item, index) in chargeItems"
:key="index"
:value="item.id"
:label="item.itemName"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="缴费周期" prop="chargePeriod">
@@ -171,7 +177,6 @@
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</el-card>
<el-dialog :title="dialog.title" v-model="dialog.visible" width="650px" append-to-body>
<div class="payInfo">
<div>
@@ -237,7 +242,7 @@
<el-tooltip class="box-item" effect="light" placement="top">
<el-icon color="#909399" style="vertical-align: middle"><QuestionFilled /></el-icon>
<template #content>
<span>有账单 应缴费用 不足 优惠金额</span>
<span>有账单 应缴费用 不足 优惠金额</span>
</template>
</el-tooltip>
</div>
@@ -418,8 +423,7 @@ import { getCommunitylistAPI } from '@/api/system/community';
import { billEdit, billlistType, billPrint, BillPrintType, billQuery } from '@/api/system/SdbCashier';
import { listChargeItem } from '@/api/system/SdbChargeItem';
import { ref } from 'vue';
// import { printTicket, printTicketDiv } from '@/utils/print';
import { add, div, eq, eqMoney, gtMoney, ltMoney, maxMoney, minMoney, moneyAdd, mul, sub, sum, toBig } from '@/utils/money';
import { add, div, eq, eqMoney, gtMoney, ltMoney, minMoney, moneyAdd, mul, sub, sum, toBig } from '@/utils/money';
import { BuildingUnitlist, BuildingVo, FloorsType } from '@/api/system/house/type';
import { ChargeItemVO } from '@/api/system/SdbChargeItem/type';
import { communitylist_rows_type } from '@/api/system/community/type';
@@ -429,8 +433,8 @@ import { CirclePlus, Delete, QuestionFilled } from '@element-plus/icons-vue';
import { useUserStore } from '@/store/modules/user';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_pay_status, com_pay_method, com_charge_item_unit, com_bill_charge_period } = toRefs<any>(
proxy?.useDict('com_pay_status', 'com_pay_method', 'com_charge_item_unit', 'com_bill_charge_period')
const { com_pay_status, com_pay_method, com_charge_item_unit } = toRefs<any>(
proxy?.useDict('com_pay_status', 'com_pay_method', 'com_charge_item_unit')
);
const userStore = useUserStore();
@@ -461,20 +465,34 @@ const villageId = localStorage.getItem('villageid') ?? undefined;
const queryParams = ref({
pageNum: 1,
pageSize: 10,
// 收款状态
payStatus: null,
// 小区id
villageId: villageId,
// 楼栋id
buildingId: undefined,
// 单元id
unitNoId: undefined,
// 房屋id
houseId: undefined,
// 缴费周期
chargePeriod: undefined,
// 房屋类型id
houseUseTypeId: undefined,
// 缴费时间
payTime: undefined,
// 缴费人
residentName: undefined,
// 缴费项目id
chargeItemId: undefined,
period: undefined,
// 缴费时间开始
payTimeBegin: undefined,
// 缴费时间结束
payTimeEnd: undefined,
// 开始时间
startDate: undefined,
// 结束时间
endDate: undefined
});
@@ -510,6 +528,7 @@ function handleChangeVillage(val: string) {
queryParams.value.unitNoId = undefined;
queryParams.value.buildingId = undefined;
queryParams.value.chargeItemId = undefined;
queryParams.value.houseId = undefined;
buildingList.value = [];
unitList.value = [];
if (val) {
@@ -523,7 +542,8 @@ function handleChangeVillage(val: string) {
// 获取收费项目
function handleChargeItem(id: string) {
listChargeItem({
villageId: id
villageId: id,
status: '0'
}).then((res) => {
chargeItems.value = res.rows;
});
@@ -582,6 +602,8 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
payTime.value = [];
chargePeriod.value = [];
queryParams.value = {
chargePeriod: undefined,
payTime: undefined,
@@ -601,6 +623,7 @@ const resetQuery = () => {
startDate: undefined,
endDate: undefined
};
queryParams.value.villageId = villageId;
handleQuery();
};
// 控制收费 按钮 不能有已缴纳的(后端会校验)
@@ -998,11 +1021,13 @@ function conFimPrint() {
}
onMounted(() => {
queryParams.value.villageId = villageId;
getList();
getHouseTypeList();
// 全部小区
getCommunitylistAPI().then((res) => {
villageList.value = res.rows;
handleChangeVillage(queryParams.value.villageId);
});
});

View File

@@ -30,19 +30,25 @@
<div class="main">{{ form.visitorPhone }}</div>
</div>
<div class="carinfo_item">
<div class="label">访问日期:</div>
<div class="label">访问人数:</div>
<div class="main" style="text-wrap: wrap">
<span>{{ form.startTime }}</span>
<span>{{ form.visitorNum }}</span>
</div>
</div>
<div class="carinfo_item">
<div class="label">访问时长:</div>
<div class="main">{{ formatTimeDiff(form.startTime, form.endTime) }}</div>
</div>
<div class="carinfo_item">
<div class="label">访问日期:</div>
<div class="main" style="text-wrap: wrap">
<span>{{ form.startTime }}</span>
</div>
</div>
<!-- <div class="carinfo_item">
<div class="label">访问业主:</div>
<div class="main flex align-items-center">
<div>去丽丽</div>
<div>去丽丽</div>
<div class="ml-20px color-blue cursor-pointer">住户详情</div>
</div>
</div> -->

View File

@@ -278,7 +278,7 @@ const rules = ref({
{
validator: (_: any, value: string | number, callback: (arg0?: Error) => void) => {
if (value === '' || value === null || value === 0) {
return callback(new Error('请输入房屋面积'));
return callback();
}
const val = Number(value);
if (Number.isNaN(val)) {
@@ -371,7 +371,8 @@ const handleExceed = () => {
ElMessage.warning('最多上传9张图片');
};
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
dialogImageUrl.value = uploadFile.url!;
dialogImageUrl.value = '';
dialogImageUrl.value = uploadFile.url;
dialogVisible.value = true;
};
const handleRemove: UploadProps['onRemove'] = (_, uploadFiles) => {

View File

@@ -142,7 +142,7 @@ const exportChecked = () => {
list: listValue.join(','),
dataType: '1'
},
`巡检日报_选中${listValue.length}条数据_${new Date().toLocaleString()}.xlsx`,
`巡检日报_选中${listValue.length}条数据_${new Date().toLocaleString()}.xlsx`
);
} else {
ElMessage.warning('请选择数据');

View File

@@ -168,7 +168,7 @@ const data = reactive<PageData<NoticeForm, NoticeQuery>>({
const { queryParams, form, rules } = toRefs(data);
/** 查询公告列表╬ */
/** 查询公告列表╬╬╬╬╬ */
const getList = async () => {
loading.value = true;
listNotice(queryParams.value)

View File

@@ -12,7 +12,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="公告标题" prop="noticeTitle">
<el-input maxlength="20" v-model.trim="form.noticeTitle" placeholder="请输入公告标题" />
<el-input maxlength="50" v-model.trim="form.noticeTitle" placeholder="请输入公告标题" />
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->

View File

@@ -42,7 +42,7 @@
<el-row>
<el-col :span="11">
<el-form-item label="公摊面积" prop="shareArea">
<el-input maxlength="8" v-model="form.shareArea" placeholder="请输入公摊面积" >
<el-input maxlength="8" v-model="form.shareArea" placeholder="请输入公摊面积">
<template #append>
<span></span>
</template>
@@ -52,7 +52,7 @@
<el-col :span="2"></el-col>
<el-col :span="11">
<el-form-item label="实用面积" prop="internalArea">
<el-input maxlength="8" v-model="form.internalArea" placeholder="请输入实用面积" >
<el-input maxlength="8" v-model="form.internalArea" placeholder="请输入实用面积">
<template #append>
<span></span>
</template>