Compare commits

5 Commits

Author SHA1 Message Date
Zy
2d9ffb03cd 10/13 导航栏上显示小区名称,用户名称 2026-10-13 14:30:33 +08:00
Zy
2f70107415 9/21 房屋信息-账单列表-对于部分缴费添加详细缴费信息,帐单列表添加分页; 2026-09-21 15:47:02 +08:00
Zy
f6a7eb1b4e 9/20 2026-09-20 08:57:53 +08:00
Zy
292ee1a6dd 9/10 2026-09-10 16:12:22 +08:00
Zy
e8564269b0 9/2 禅道问题 2026-09-02 17:32:15 +08:00
120 changed files with 3359 additions and 2293 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.0.222:8080'
# 开发环境 接口代理地址 # 开发环境 接口代理地址
VITE_APP_PROXY_API='http://192.168.1.6:8080' #VITE_APP_PROXY_API='http://192.168.0.199:8080'
# 图片基础地址 # 图片基础地址
VITE_IMG_URL='http://192.168.1.222:8080' VITE_IMG_URL='http://192.168.1.222:8080'
@@ -35,7 +35,7 @@ VITE_LOGIN_BACK_URL_WORKBENCH_TRUE=false
VITE_APP_SHOW_WORK_PLATFORM=true VITE_APP_SHOW_WORK_PLATFORM=true
# 地图类型 天地图 WGS84 高德地图 GCJ02, 高德地图 需要在高德地图官网申请key # 地图类型 天地图 WGS84 高德地图 GCJ02, 高德地图 需要在高德地图官网申请key
VITE_APP_MAP_TYPE='GCJ02' VITE_APP_MAP_TYPE='WGS84'
# 高德地图key 建议放入环境变量中,避免泄露 # 高德地图key 建议放入环境变量中,避免泄露
VITE_GCJ02_KEY_GD='8e302fe1be5f4db62bc734db23dca149' VITE_GCJ02_KEY_GD='8e302fe1be5f4db62bc734db23dca149'

View File

@@ -14,6 +14,13 @@ export const listItem = (query?: Request): AxiosPromise<any[]> => {
params: query params: query
}); });
}; };
export const overview = (query?: Request): AxiosPromise<any[]> => {
return request({
url: '/inspection/data/generate/overview',
method: 'POST',
params: query
});
};
export interface Request { export interface Request {
/** /**
* 进行中的任务数 * 进行中的任务数

View File

@@ -82,8 +82,8 @@ export interface RecordQuery extends PageQuery {
*/ */
taskName?: string; taskName?: string;
taskTimeBegin?: ''; taskTimeBegin?: string;
taskTimeEnd?: ''; taskTimeEnd?: string;
} }
export interface DetailsVo { export interface DetailsVo {

View File

@@ -154,7 +154,6 @@ export interface QuestionComponentItem {
type: string; type: string;
title: string; title: string;
required: boolean; required: boolean;
options: QuestionComponentOption[]; options: QuestionComponentOption[];
} }

View File

@@ -23,7 +23,7 @@ export const cashQuery = (data: { residentName?: string; queryType: number }): A
export type billlistType = { export type billlistType = {
/** /**
* 类型 0物业费 1车位管理费 2垃圾处理费 3租金 4押金 * 类型 0 物业费 1 车位管理费 2 垃圾处理费 3 租金 4 押金
*/ */
detailsType: string; detailsType: string;
unitNoId: string; unitNoId: string;
@@ -57,7 +57,7 @@ export type billlistType = {
* @returns {*} * @returns {*}
* @param data * @param data
*/ */
export const billQuery = (data: { residentId?: string; payStatus: '0' | '1' }): AxiosPromise<billlistType[]> => { export const billQuery = (data: { residentId?: string; payStatus?: '0' | '1' }): AxiosPromise<billlistType[]> => {
return request({ return request({
url: '/cash/billList', url: '/cash/billList',
method: 'post', method: 'post',
@@ -84,9 +84,11 @@ export const billPrint = (data: { 'detailsIds': string[]; 'residentId': string }
export const getAllAccount = ( export const getAllAccount = (
data = {} data = {}
): AxiosPromise<{ ): AxiosPromise<{
/** 总金额 */ /** 原始金额之和(应收+优惠) */
originalAmount: string;
/** 总 应收金额 */
'receivableAmount': string; 'receivableAmount': string;
/** 总实收金额 */ /** 总已收金额 */
'receivedAmount': string; 'receivedAmount': string;
/** 总优惠金额 */ /** 总优惠金额 */
'discountAmount': string; 'discountAmount': string;
@@ -111,7 +113,7 @@ export const billDelete = (ids: string | string[]) => {
export type BillPrintType = { export type BillPrintType = {
'totalAccount': string; 'totalAccount': string;
'residentName': string; 'residentName': string;
'payTypeSummary': { 'payTypeSummary'?: {
'payType': string; 'payType': string;
'payAmount': string; 'payAmount': string;
}[]; }[];
@@ -128,8 +130,8 @@ export type BillPrintType = {
'remark': string; 'remark': string;
}[]; }[];
'printDate': string; 'printDate': string;
totalDiscountAmount: string; totalDiscountAmount?: string;
totalActualAccount: string; totalActualAccount?: string;
'HouseName': string; 'HouseName': string;
'title': string; 'title': string;
}; };

View File

@@ -96,3 +96,63 @@ export const ElectricityDeviceUnbind = (deviceCode: string) => {
method: 'PUT' method: 'PUT'
}); });
}; };
/**
* 充值
*/
export const ElectricityDeviceRecharge = (data: { 'deviceCode': string; 'amount': number }) => {
return request({
url: '/electricityDevice/recharge',
method: 'POST',
data
});
};
/**
* 设置电表价格
*/
export const setElectricityDevicePrice = (data: { 'deviceCode': string; 'price': number }) => {
return request({
url: '/electricityDevice/setPrice',
method: 'POST',
data
});
};
/**
* 设置电表开关状态
*/
export const setElectricityDeviceSwitchStatus = (data: { 'deviceCode': string; 'status': number }) => {
return request({
url: '/electricityDevice/controlSwitch',
method: 'POST',
data
});
};
/**
* 电表数据上传
*/
export const updateElectricityDeviceData = (data: { 'deviceCode': string }) => {
return request({
url: '/electricityDevice/triggerReport',
method: 'POST',
data
});
};
/**
* 上传电表设备
*/
export const uploadElectricityDevices = (data: FormData) => {
return request({
url: '/electricityDevice/importData',
method: 'POST',
data
});
};
/**
* 获取模板设备
*/
export const getElectricityDeviceFormartValue = () => {
return request({
url: '/electricityDevice/importTemplate',
method: 'POST'
});
};

View File

@@ -25,11 +25,30 @@ export interface ElectricityDeviceVO {
* *
*/ */
url: string; url: string;
/**
* 电价
*/
price: number | null;
/** /**
* *
*/ */
openid: string; openid: string;
electricityRecord?: {
'id': string;
'deviceCode': string;
'totalEnergy': string;
'readTime': string;
'balance': string;
'status': string;
'event': string;
'rangeTime': string;
'rangeFee': number;
'price': number | null;
'type': number | null;
'updateTime': string | null;
};
} }
export interface ElectricityDeviceForm extends BaseEntity { export interface ElectricityDeviceForm extends BaseEntity {

View File

@@ -18,6 +18,7 @@ export interface MaterialVO {
* 规格型号* * 规格型号*
*/ */
model: string; model: string;
villageId: string;
/** /**
* 计量单位 * 计量单位

View File

@@ -33,6 +33,7 @@ export interface SupplierVO {
* 备注 * 备注
*/ */
remark: string; remark: string;
villageId: string;
} }
export interface SupplierForm extends BaseEntity { export interface SupplierForm extends BaseEntity {

View File

@@ -134,16 +134,20 @@ export interface BillResidentList {
} }
export interface House { export interface House {
buildingName: string; 'buildingName': string;
floorNo: number; 'unitNoName': string;
houseArea: string; 'houseId': string;
houseId: number; 'houseType': string;
houseNo: string; 'houseNo': string;
houseType: string; 'houseArea': string;
internalArea: string; 'floorNo': number;
shareArea: string; 'unitNoId': string;
unitNoName: string; 'villageName': string;
villageName: string; 'villageId': string;
'buildingId': string;
internalArea?: string;
shareArea?: string;
} }
export interface ResidentList { export interface ResidentList {

View File

@@ -17,7 +17,7 @@ export type Datum = {
houseName: string; houseName: string;
payTime: string; payTime: string;
propertyOffsetAmount: string; propertyOffsetAmount: string;
remark: null; remark: string;
residentName: string; residentName: string;
transferAmount: string; transferAmount: string;
unionPayAmount: string; unionPayAmount: string;

View File

@@ -1,4 +1,5 @@
import request from '@/utils/request'; import request from '@/utils/request';
import { AxiosPromise } from 'axios';
/** /**
* 查询小区概况 * 查询小区概况
@@ -335,7 +336,16 @@ type LifeIndices = {
* 查询 快捷入口 * 查询 快捷入口
* @returns {*} * @returns {*}
*/ */
export const getQuicklyshortcut = (query?: {}) => { export const getQuicklyshortcut = (query?: {}): AxiosPromise<
{
id: string;
menuId: string;
menuName: string;
menuPath: string;
menuIcon: string;
sortOrder: number;
}[]
> => {
return request({ return request({
url: '/workSpace/shortcut/list', url: '/workSpace/shortcut/list',
method: 'GET', method: 'GET',

View File

@@ -273,7 +273,6 @@ defineExpose({
parkingId: id parkingId: id
}) as BillUserlist }) as BillUserlist
); );
console.log(multipleSelection.value);
} else { } else {
multipleSelection.value = []; multipleSelection.value = [];
} }

View File

@@ -100,7 +100,7 @@ const props = defineProps({
}, },
userType: { userType: {
type: String, type: String,
default: 'complainUser' default: ''
}, },
isMultiple: { isMultiple: {
type: Boolean, type: Boolean,
@@ -222,7 +222,6 @@ function handleSelectionChange(selection: tableType[]) {
const uniqueMap = new Map(); const uniqueMap = new Map();
finalSelection.forEach((item) => uniqueMap.set(item.id, item)); finalSelection.forEach((item) => uniqueMap.set(item.id, item));
multipleSelection.value = Array.from(uniqueMap.values()); multipleSelection.value = Array.from(uniqueMap.values());
console.log(multipleSelection.value.map((item) => item.id));
} else { } else {
// --- 单选逻辑 --- // --- 单选逻辑 ---
if (selection.length > 0) { if (selection.length > 0) {

View File

@@ -301,7 +301,6 @@ defineExpose({
...queryParams.value, ...queryParams.value,
...params ...params
}; };
console.log(queryParams.value);
getList(); getList();
dialog.value.dialogVisible = true; dialog.value.dialogVisible = true;
} }

View File

@@ -19,19 +19,21 @@
<el-button @click="resetQuery">重置</el-button> <el-button @click="resetQuery">重置</el-button>
</div> </div>
</nav> </nav>
<el-table <el-table ref="multipleTableRef" :data="tableData" border>
ref="multipleTableRef" <el-table-column align="center" width="55">
:data="tableData" <template #header>
border <el-checkbox v-model="isSelectAll" :disabled="!isMultiple" :indeterminate="isIndeterminate" @change="handleSelectAllChange" />
@select-all="handleSelectionChange" </template>
@row-click="handleRowClick" <template #default="scope">
@selection-change="handleSelectionChange" <el-checkbox :model-value="isRowSelected(scope.row)" @change="(val: boolean) => handleRowCheckChange(scope.row, val)" />
> </template>
<el-table-column align="center" type="selection" width="55" /> </el-table-column>
<el-table-column align="center" label="物料名称" prop="materialName" /> <el-table-column align="center" label="物料名称" prop="materialName" />
<el-table-column align="center" label="品牌" prop="brand" /> <el-table-column align="center" label="品牌" prop="brand" />
<el-table-column align="center" label="规格型号" prop="model" /> <el-table-column align="center" label="规格型号" prop="model" />
<el-table-column align="center" label="单位" prop="unit" /> <el-table-column align="center" label="单位" prop="unit" />
<el-table-column v-if="!isHidden" align="center" label="数量" prop="stockNum" />
<el-table-column align="center" label="所属小区" prop="villageName" />
<el-table-column align="center" label="备注" prop="remark" /> <el-table-column align="center" label="备注" prop="remark" />
</el-table> </el-table>
@@ -47,7 +49,7 @@
/> />
<div> <div>
<el-button @click="conback">退出</el-button> <el-button @click="conback">退出</el-button>
<el-button type="primary" @click="confirm"> 确定 </el-button> <el-button :loading="loading" type="primary" @click="confirm"> 确定 </el-button>
</div> </div>
</div> </div>
</template> </template>
@@ -57,7 +59,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { listMaterial } from '@/api/system/Tmaterial'; import { listMaterial } from '@/api/system/Tmaterial';
import { MaterialVO } from '@/api/system/Tmaterial/type'; import { MaterialVO } from '@/api/system/Tmaterial/type';
import { ref, nextTick, getCurrentInstance, ComponentInternalInstance, toRefs } from 'vue'; import { ComponentInternalInstance, getCurrentInstance, ref, toRefs } from 'vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -77,6 +79,10 @@ const props = defineProps({
isMultiple: { isMultiple: {
type: Boolean, type: Boolean,
default: false default: false
},
isHidden: {
type: Boolean,
default: true
} }
}); });
@@ -104,41 +110,87 @@ const tableData = ref<MaterialVO[]>([]);
const total = ref(0); const total = ref(0);
const loading = ref(false); const loading = ref(false);
// 存储选中项 /** 存储所有选中项(跨页累计,非全选模式使用) */
const multipleSelection = ref<MaterialVO[]>([]); const multipleSelection = ref<MaterialVO[]>([]);
// 多选 /** 是否已跨页全选(标记模式,不实际持有全部数据) */
const isSelectAll = ref(false);
/** 表头复选框半选状态(部分选中时为 true) */
const isIndeterminate = ref(false);
/** 全选模式下被手动取消勾选的 id 列表 */
const deselectedIds = ref<string[]>([]);
const isMultiple = computed(() => props.isMultiple ?? false); const isMultiple = computed(() => props.isMultiple ?? false);
// 优化 handleUserSelection,避免重复查找 /** 判断某行是否已选中 */
const handleUserSelection = () => { function isRowSelected(row: MaterialVO): boolean {
if (!userid.value || !tableData.value.length) return; if (isSelectAll.value) {
// 全选模式:只要不在取消列表中就算选中
return !deselectedIds.value.includes(row.id);
}
return multipleSelection.value.some((item) => item.id === row.id);
}
/** 更新表头复选框状态:全选 / 半选 / 空 */
function updateIndeterminate() {
if (isSelectAll.value) {
// 全选模式下:有取消勾选的行 → 半选,否则 → 全选
isIndeterminate.value = deselectedIds.value.length > 0;
return;
}
// 非全选模式:只要有已勾选的行即为半选。
// 注意:不能用 selectedCount >= total 去做“自动升级为全选”,
// 否则搜索/筛选后 total 变小,会把之前积累的 multipleSelection 误判并清空,覆盖已选数据。
isIndeterminate.value = multipleSelection.value.length > 0;
}
/** 跨页全选 / 取消全选(仅设标记,不拉取数据) */
function handleSelectAllChange(val: boolean) {
isSelectAll.value = val;
deselectedIds.value = [];
multipleSelection.value = []; multipleSelection.value = [];
if (userid.value.trim() !== '') { updateIndeterminate();
const arr = userid.value.split(','); }
arr.forEach((item) => {
const target = tableData.value.find((row) => row.id == item);
target && multipleSelection.value.push(target);
});
nextTick(() => {
if (multipleTableRef.value) {
multipleTableRef.value.clearSelection();
multipleSelection.value.forEach((item) => {
multipleTableRef.value.toggleRowSelection(item, true);
});
}
});
}
};
// 监听 /** 单行勾选 / 取消勾选 */
// 监听优化:只在 userid 变化时触发 function handleRowCheckChange(row: MaterialVO, checked: boolean) {
// watch(userid, handleUserSelection, { immediate: true }); if (!isMultiple.value) {
// watch(tableData, () => { // 单选模式:仅保留当前行
// if (userid.value) handleUserSelection(); // 仅在有 userid 时重新处理 if (checked) {
// }); multipleSelection.value = [row];
} else {
multipleSelection.value = [];
}
isSelectAll.value = false;
deselectedIds.value = [];
updateIndeterminate();
return;
}
if (isSelectAll.value) {
// 全选模式:维护取消列表
if (checked) {
deselectedIds.value = deselectedIds.value.filter((id) => id !== row.id);
} else {
if (!deselectedIds.value.includes(row.id)) {
deselectedIds.value.push(row.id);
}
}
} else {
// 非全选模式:维护选中列表
if (checked) {
if (!isRowSelected(row)) {
multipleSelection.value.push(row);
}
} else {
multipleSelection.value = multipleSelection.value.filter((item) => item.id !== row.id);
}
}
updateIndeterminate();
}
// ====================== 方法 ====================== // ====================== 方法 ======================
// 获取列表 // 获取列表
function getlist() { function getlist() {
loading.value = true; loading.value = true;
@@ -146,46 +198,16 @@ function getlist() {
.then((res) => { .then((res) => {
tableData.value = res.rows; tableData.value = res.rows;
total.value = res.total; total.value = res.total;
updateIndeterminate();
})
.catch((error) => {
console.error('获取物料列表失败:', error);
}) })
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;
}); });
} }
// ==============================================
// ✅ 新增:处理 selection-change(多选专用)
// ==============================================
function handleSelectionChange(selection: MaterialVO[]) {
if (isMultiple.value) {
multipleSelection.value = selection;
}
}
// ==============================================
// 最佳单选方案:点击行选中,无报错、无循环、最稳定
// ==============================================
// 优化 handleRowClick,避免重复
function handleRowClick(row: MaterialVO) {
if (!multipleTableRef.value) return;
if (isMultiple.value) {
const index = multipleSelection.value.findIndex((item) => item.id === row.id);
if (index > -1) {
// 取消选中
multipleSelection.value.splice(index, 1);
multipleTableRef.value?.toggleRowSelection(row, false);
} else {
// 选中
multipleSelection.value.push(row);
multipleTableRef.value?.toggleRowSelection(row, true);
}
} else {
// 【单选模式】先清空,再选中当前行
multipleSelection.value = [];
multipleTableRef.value.clearSelection();
multipleSelection.value = [row];
multipleTableRef.value.toggleRowSelection(row, true);
}
}
// 重置查询 // 重置查询
function resetQuery() { function resetQuery() {
queryParams.value = { queryParams.value = {
@@ -194,6 +216,7 @@ function resetQuery() {
brand: '', brand: '',
materialName: '' materialName: ''
}; };
resetSelection();
getlist(); getlist();
} }
@@ -203,44 +226,90 @@ function conback() {
resetQuery(); resetQuery();
} }
// ============================================== /** 重置所有选中状态 */
// ✅ 核心修改:根据 returnvalue 返回对应的值 function resetSelection() {
// ============================================== multipleSelection.value = [];
function confirm() { isSelectAll.value = false;
dialog.value.dialogVisible = false; isIndeterminate.value = false;
deselectedIds.value = [];
}
/** 根据 returnvalue 发射选中结果 */
function emitResult(data: MaterialVO[]) {
if (isMultiple.value) { if (isMultiple.value) {
// 多选
if (returnvalue.value === '*') { if (returnvalue.value === '*') {
// ✅ 返回整个对象数组 emit('change', data);
emit('change', multipleSelection.value);
} else { } else {
// 返回指定字段数组
emit( emit(
'change', 'change',
multipleSelection.value.map((item) => item[returnvalue.value as keyof MaterialVO]) data.map((item) => item[returnvalue.value as keyof MaterialVO])
); );
} }
} else { } else {
// 单选
if (returnvalue.value === '*') { if (returnvalue.value === '*') {
// ✅ 返回整个对象 emit('change', data[0]);
emit('change', multipleSelection.value[0]);
} else { } else {
// 返回指定字段 emit('change', data[0]?.[returnvalue.value as keyof MaterialVO]);
emit('change', multipleSelection.value[0][returnvalue.value as keyof MaterialVO]);
} }
} }
}
/** 确定:全选模式下按需拉取全部数据,非全选模式直接使用已选列表 */
async function confirm() {
if (isMultiple.value && isSelectAll.value && total.value > 0) {
// 全选模式:此时才真正拉取全部数据,排除已取消勾选的行
loading.value = true;
try {
const res = await listMaterial({
...queryParams.value,
pageNum: 1,
pageSize: total.value
});
const allData: MaterialVO[] = res.rows || [];
const filteredData = allData.filter((item) => !deselectedIds.value.includes(item.id));
dialog.value.dialogVisible = false;
emitResult(filteredData);
} catch (error) {
console.error('获取全部物料数据失败:', error);
} finally {
loading.value = false;
}
} else {
// 非全选模式:直接使用已收集的选中列表
dialog.value.dialogVisible = false;
emitResult(multipleSelection.value);
}
multipleSelection.value = []; resetSelection();
multipleTableRef.value.clearSelection();
} }
// 初始化
getlist();
defineExpose({ defineExpose({
open: () => { open: () => {
// 重置查询条件,避免残留上次的筛选条件
queryParams.value = {
pageNum: 1,
pageSize: 10,
brand: '',
materialName: ''
};
// 立即清空旧列表数据,避免闪烁
tableData.value = [];
total.value = 0;
// 重置全选标记
isSelectAll.value = false;
deselectedIds.value = [];
// 根据 userid(逗号分隔)初始化选中状态
const arr = userid.value ? userid.value.split(',').filter((id) => id) : [];
if (arr.length > 0) {
multipleSelection.value = arr.map((id) => ({ id }) as MaterialVO);
isIndeterminate.value = true;
} else {
multipleSelection.value = [];
isIndeterminate.value = false;
}
getlist();
dialog.value.dialogVisible = true; dialog.value.dialogVisible = true;
handleUserSelection();
} }
}); });
</script> </script>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div>
<slot v-if="$slots.default" /> <slot v-if="$slots.default" />
<div v-else class="residentBox" @click="choiceResidentFun"> <div v-else class="residentBox" @click="choiceResidentFun">
<div v-if="multipleSelection.length === 0 && props.username === null" class="defaultBox"> <div v-if="multipleSelection.length === 0 && props.username === null" class="defaultBox">
<span>请选择</span> <span>请选择</span>
@@ -10,7 +10,6 @@
<span>姓名:{{ multipleSelection[0]?.nickName ?? props.username }}</span> <span>姓名:{{ multipleSelection[0]?.nickName ?? props.username }}</span>
</div> </div>
</div> </div>
<el-dialog v-model="dialog.dialogVisible" :title="dialog.dialogTitle" append-to-body width="1050"> <el-dialog v-model="dialog.dialogVisible" :title="dialog.dialogTitle" append-to-body width="1050">
<nav class="flex flex-items-center mb-8"> <nav class="flex flex-items-center mb-8">
<div class="flex flex-items-center"> <div class="flex flex-items-center">
@@ -66,6 +65,7 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -236,8 +236,6 @@ function handleSelectionChange(selection: tableType[]) {
multipleSelection.value = []; multipleSelection.value = [];
} }
} }
console.log('handleSelectionChange', selection);
} }
// 重置查询 // 重置查询
function resetQuery() { function resetQuery() {

View File

@@ -143,6 +143,7 @@ const multipleTableRef = ref();
const tableData = ref<BillUserlist[]>([]); const tableData = ref<BillUserlist[]>([]);
const total = ref(0); const total = ref(0);
const loading = ref(false); const loading = ref(false);
/** 存储所有选中项(跨页累计,非全选模式使用) */ /** 存储所有选中项(跨页累计,非全选模式使用) */
const multipleSelection = ref<BillUserlist[]>([]); const multipleSelection = ref<BillUserlist[]>([]);
/** 是否已跨页全选(标记模式,不实际持有全部数据) */ /** 是否已跨页全选(标记模式,不实际持有全部数据) */

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="quarter-select"> <div class="quarter-select">
<!-- 输入框,点击弹出popover --> <!-- 输入框,点击弹出popover -->
<el-popover v-model:visible="popoverVisible" placement="bottom-start" trigger="manual" width="240"> <el-popover v-model:visible="popoverVisible" placement="bottom-start" trigger="click" width="240">
<div class="quarter-panel"> <div class="quarter-panel">
<!-- 年份切换 --> <!-- 年份切换 -->
<div class="year-header"> <div class="year-header">
@@ -14,7 +14,7 @@
<el-button <el-button
v-for="q in 4" v-for="q in 4"
:key="q" :key="q"
:disabled="isDisabledQuarter(currentYear, q)" :disabled="isDisabledQuarter()"
:type="isSelected(currentYear, q) ? 'primary' : ''" :type="isSelected(currentYear, q) ? 'primary' : ''"
style="margin: 0" style="margin: 0"
@click="handleSelect(currentYear, q)" @click="handleSelect(currentYear, q)"
@@ -55,7 +55,7 @@ const emit = defineEmits(['update:modelValue', 'change']);
const popoverVisible = ref(false); const popoverVisible = ref(false);
// 解析回显的季度值 2026‑1 // 解析回显的季度值 2026‑1
const parseQuarter = (str) => { const parseQuarter = (str: any) => {
if (!str) return { year: new Date().getFullYear(), quarter: null }; if (!str) return { year: new Date().getFullYear(), quarter: null };
const [y, q] = str.split('-').map(Number); const [y, q] = str.split('-').map(Number);
return { year: y, quarter: q }; return { year: y, quarter: q };
@@ -67,13 +67,12 @@ const currentYear = ref(initYear);
// 输入框展示文本 // 输入框展示文本
const displayText = computed(() => { const displayText = computed(() => {
if (!props.modelValue) return ''; if (!props.modelValue) return '';
console.log(props.modelValue, props.modelValue.trim().split('-'));
const [y, q] = props.modelValue.split('-').map(Number); const [y, q] = props.modelValue.split('-').map(Number);
return `${y}年${q}季度`; return `${y}年${q}季度`;
}); });
// 是否选中该季度 // 是否选中该季度
const isSelected = (y, q) => { const isSelected = (y: any, q: any) => {
return props.modelValue === `${y}-${q}`; return props.modelValue === `${y}-${q}`;
}; };
@@ -90,7 +89,7 @@ const prevYear = () => currentYear.value--;
const nextYear = () => currentYear.value++; const nextYear = () => currentYear.value++;
// 选中季度 // 选中季度
const handleSelect = (y, q) => { const handleSelect = (y: any, q: any) => {
const val = `${y}-${q}`; const val = `${y}-${q}`;
emit('update:modelValue', val); emit('update:modelValue', val);
emit('change', val); emit('change', val);

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="step-timeline"> <div class="step-timeline">
<div v-for="(item, index) in steps" :key="index" class="step-item" :class="item.status"> <div v-for="(item, index) in steps" :key="index" :class="item.status" class="step-item">
<div class="step-node"> <div class="step-node">
<div class="step-circle"> <div class="step-circle">
<el-icon v-if="item.status === 'completed'" :size="12"><Check /></el-icon> <el-icon v-if="item.status === 'completed'" :size="12"><Check /></el-icon>
@@ -10,23 +10,23 @@
</div> </div>
<div class="step-content"> <div class="step-content">
<div class="step-title"> <div class="step-title">
<slot name="title" :item="item" :index="index"></slot> <slot :index="index" :item="item" name="title"></slot>
</div> </div>
<div v-if="item.wrap === true"> <div v-if="item.wrap === true">
<div class="step-subtitle"> <div class="step-subtitle">
<slot name="subtitle" :item="item" :index="index"></slot> <slot :index="index" :item="item" name="subtitle"></slot>
</div> </div>
<div class="step-time"> <div class="step-time">
<slot name="time" :item="item" :index="index"></slot> <slot :index="index" :item="item" name="time"></slot>
</div> </div>
</div> </div>
<div v-else> <div v-else>
<span> <span>
<slot name="subtitle" :item="item" :index="index"></slot> <slot :index="index" :item="item" name="subtitle"></slot>
</span> </span>
&nbsp;&nbsp; <span>&nbsp;&nbsp;</span>
<span> <span>
<slot name="time" :item="item" :index="index"></slot> <slot :index="index" :item="item" name="time"></slot>
</span> </span>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="navbar" :class="'nav' + navType"> <div :class="'nav' + navType" class="navbar">
<hamburger <hamburger
v-show="Showhamburger" v-show="Showhamburger"
id="hamburger-container" id="hamburger-container"
@@ -8,14 +8,15 @@
@toggle-click="toggleSideBar" @toggle-click="toggleSideBar"
> >
</hamburger> </hamburger>
<div style="color: grey; margin-right: 20px">{{ currentVillageInfo.villageName }}</div>
<div class="flex-1 h-full"></div> <div class="flex-1 h-full"></div>
<div class="right-menu flex align-center"> <div class="right-menu flex align-center">
<template v-if="appStore.device !== 'mobile'"> <template v-if="appStore.device !== 'mobile'">
<el-tooltip :content="proxy.$t('navbar.message')" effect="dark" placement="bottom"> <el-tooltip :content="proxy.$t('navbar.message')" effect="dark" placement="bottom">
<div> <div>
<el-popover placement="bottom-end" trigger="click" transition="el-zoom-in-top" :width="400" :persistent="false"> <el-popover :persistent="false" :width="400" placement="bottom-end" transition="el-zoom-in-top" trigger="click">
<template #reference> <template #reference>
<el-badge :value="newNotice > 0 ? newNotice : ''" :max="99"> <el-badge :max="99" :value="newNotice > 0 ? newNotice : ''">
<div class="right-menu-item hover-effect" style="display: block"><svg-icon icon-class="message" /></div> <div class="right-menu-item hover-effect" style="display: block"><svg-icon icon-class="message" /></div>
</el-badge> </el-badge>
</template> </template>
@@ -29,15 +30,16 @@
<div class="avatar-container"> <div class="avatar-container">
<el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand"> <el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img alt="头像" :src="userStore.avatar" class="user-avatar" /> <img :src="userStore.avatar" alt="头像" class="user-avatar" />
<div class="username">{{ userStore.nickname }}</div>
<el-icon><caret-bottom /></el-icon> <el-icon><caret-bottom /></el-icon>
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item divided command="changecommunity" v-if="isShow"> <el-dropdown-item v-if="isShow" command="changecommunity" divided>
<span>{{ proxy.$t('navbar.community') }}</span> <span>{{ proxy.$t('navbar.community') }}</span>
</el-dropdown-item> </el-dropdown-item>
<router-link to="/system/user/profile" v-if="isShow" v-has-permi="['system:user:profile']"> <router-link v-if="isShow" v-has-permi="['system:user:profile']" to="/system/user/profile">
<el-dropdown-item>{{ proxy.$t('navbar.personalCenter') }}</el-dropdown-item> <el-dropdown-item>{{ proxy.$t('navbar.personalCenter') }}</el-dropdown-item>
</router-link> </router-link>
<el-dropdown-item :divided="isShow" command="logout"> <el-dropdown-item :divided="isShow" command="logout">
@@ -51,7 +53,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import hamburger from '@/components/Hamburger/index.vue'; import hamburger from '@/components/Hamburger/index.vue';
import { useAppStore } from '@/store/modules/app'; import { useAppStore } from '@/store/modules/app';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
@@ -61,14 +63,15 @@ import notice from './notice/index.vue';
import router from '@/router'; import router from '@/router';
import { ElMessageBoxOptions } from 'element-plus/es/components/message-box/src/message-box.type'; import { ElMessageBoxOptions } from 'element-plus/es/components/message-box/src/message-box.type';
import { CaretBottom } from '@element-plus/icons-vue'; import { CaretBottom } from '@element-plus/icons-vue';
import { closeWebSocket } from '@/utils/websocket';
import { closeSSE } from '@/utils/sse';
const props = defineProps({ const props = defineProps({
Showhamburger: { Showhamburger: {
type: Boolean, type: Boolean,
default: true default: true
} }
}); });
const currentVillageInfo = JSON.parse(localStorage.getItem('villageInfo') || '{}');
const appStore = useAppStore(); const appStore = useAppStore();
const userStore = useUserStore(); const userStore = useUserStore();
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
@@ -93,6 +96,9 @@ const logout = async () => {
type: 'warning' type: 'warning'
} as ElMessageBoxOptions); } as ElMessageBoxOptions);
userStore.logout().then(() => { userStore.logout().then(() => {
// 关闭长连接
closeWebSocket();
closeSSE();
router.replace({ router.replace({
path: '/login', path: '/login',
query: { query: {
@@ -166,7 +172,6 @@ watch(
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
background: #dfedff;
background: transparent; background: transparent;
//border-bottom: 1px solid #dfedff; //border-bottom: 1px solid #dfedff;
@@ -225,8 +230,7 @@ watch(
padding: 0 8px; padding: 0 8px;
height: 100%; height: 100%;
font-size: 18px; font-size: 18px;
color: #fff; color: grey;
color: #c0c4cc;
vertical-align: text-bottom; vertical-align: text-bottom;
@@ -241,9 +245,14 @@ watch(
.avatar-wrapper { .avatar-wrapper {
margin-top: 5px; margin-top: 5px;
// margin-left: 20px;
position: relative; position: relative;
display: flex;
align-items: center;
.username {
font-size: 12px;
margin-top: 10px;
margin-left: 10px;
}
.user-avatar { .user-avatar {
cursor: pointer; cursor: pointer;
width: 40px; width: 40px;

View File

@@ -2,10 +2,11 @@
<div v-if="!item.hidden"> <div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item, item.children) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow"> <template v-if="hasOneShowingChild(item, item.children) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)"> <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }"> <el-menu-item :class="{ 'submenu-title-noDropdown': !isNest }" :index="resolvePath(onlyOneChild.path)">
<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" /> <svg-icon v-if="onlyOneChild.path === '/index'" :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
<div v-else style="margin-right: 14px; width: 14px"></div>
<template #title> <template #title>
<span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span> <span :title="hasTitle(onlyOneChild.meta.title)" class="menu-title">{{ onlyOneChild.meta.title }}</span>
</template> </template>
</el-menu-item> </el-menu-item>
</app-link> </app-link>
@@ -14,22 +15,22 @@
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported> <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
<template v-if="item.meta" #title> <template v-if="item.meta" #title>
<svg-icon :icon-class="item.meta ? item.meta.icon : ''" /> <svg-icon :icon-class="item.meta ? item.meta.icon : ''" />
<span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span> <span :title="hasTitle(item.meta?.title)" class="menu-title">{{ item.meta?.title }}</span>
</template> </template>
<sidebar-item <sidebar-item
v-for="(child, index) in item.children" v-for="(child, index) in item.children"
:key="child.path + index" :key="child.path + index"
:base-path="resolvePath(child.path)"
:is-nest="true" :is-nest="true"
:item="child" :item="child"
:base-path="resolvePath(child.path)"
class="nest-menu" class="nest-menu"
/> />
</el-sub-menu> </el-sub-menu>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { isExternal } from '@/utils/validate'; import { isExternal } from '@/utils/validate';
import AppLink from './Link.vue'; import AppLink from './Link.vue';
import { getNormalPath } from '@/utils/ruoyi'; import { getNormalPath } from '@/utils/ruoyi';
@@ -49,9 +50,7 @@ const props = defineProps({
default: '' default: ''
} }
}); });
const onlyOneChild = ref<any>({}); const onlyOneChild = ref<any>({});
const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => { const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => {
if (!children) { if (!children) {
children = []; children = [];

View File

@@ -4,24 +4,25 @@
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper"> <el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
<transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in"> <transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in">
<el-menu <el-menu
:default-active="activeMenu" :active-text-color="theme"
:collapse="isCollapse"
:background-color="bgColor" :background-color="bgColor"
:collapse="isCollapse"
:collapse-transition="false"
:default-active="activeMenu"
:popper-offset="12"
:text-color="textColor" :text-color="textColor"
:unique-opened="true" :unique-opened="true"
:active-text-color="theme"
:collapse-transition="false"
:popper-offset="12"
mode="vertical" mode="vertical"
@select="handleMenuSelect"
> >
<sidebar-item v-for="(r, index) in sidebarRouters" :key="r.path + index" :item="r" :base-path="r.path" /> <sidebar-item v-for="(r, index) in sidebarRouters" :key="r.path + index" :base-path="r.path" :item="r" />
</el-menu> </el-menu>
</transition> </transition>
</el-scrollbar> </el-scrollbar>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import Logo from './Logo.vue'; import Logo from './Logo.vue';
import SidebarItem from './SidebarItem.vue'; import SidebarItem from './SidebarItem.vue';
import variables from '@/assets/styles/variables.module.scss'; import variables from '@/assets/styles/variables.module.scss';
@@ -54,4 +55,22 @@ const activeMenu = computed(() => {
const bgColor = '#dfedff'; const bgColor = '#dfedff';
// const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground)); // const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground));
const textColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor)); const textColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor));
const router = useRouter();
// UI层菜单点击锁
const menuLoading = ref(false);
const handleMenuSelect = async (path: string) => {
if (menuLoading.value) return;
if (router.currentRoute.value.path === path) return;
try {
menuLoading.value = true;
await router.push(path);
} catch (e) {
// 捕获导航取消异常
} finally {
menuLoading.value = false;
}
};
</script> </script>

View File

@@ -1,38 +1,31 @@
<template> <template>
<!-- 👇 加这一层全局缩放容器 --> <!-- 👇 加这一层全局缩放容器 -->
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }"> <div :class="classObj" :style="{ '--current-color': theme }" class="app-wrapper">
<side-bar class="sidebar-container" /> <side-bar class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container" id="main-container"> <div id="main-container" :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
<div class="navbarContant"> <div class="navbarContant">
<navbar ref="navbarRef" @set-layout="setLayout" /> <navbar ref="navbarRef" @set-layout="setLayout" />
</div> </div>
<app-main /> <app-main />
<i class="bgColor" v-if="bgColorStore.isShow"></i> <i v-if="bgColorStore.isShow" class="bgColor"></i>
<settings ref="settingRef" /> <settings ref="settingRef" />
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup>
<script setup lang="ts">
// ==========================================
// ✅ 第1步:统一 import
// ==========================================
import { ref, computed, watchEffect, onMounted, onUnmounted } from 'vue'; import { ref, computed, watchEffect, onMounted, onUnmounted } from 'vue';
import { useWindowSize } from '@vueuse/core'; import { useWindowSize } from '@vueuse/core';
import SideBar from './components/Sidebar/index.vue'; import SideBar from './components/Sidebar/index.vue';
import { AppMain, Navbar, Settings } from './components'; import { AppMain, Navbar, Settings } from './components';
import { useAppStore } from '@/store/modules/app'; import { useAppStore } from '@/store/modules/app';
import { useSettingsStore } from '@/store/modules/settings'; import { useSettingsStore } from '@/store/modules/settings';
// import { NavTypeEnum } from '@/enums/NavTypeEnum';
import { useBgColor } from '@/store/modules/bgColor'; import { useBgColor } from '@/store/modules/bgColor';
import { initWebSocket } from '@/utils/websocket'; import { initWebSocket, closeWebSocket } from '@/utils/websocket'; // 新增 closeWebSocket
import { initSSE } from '@/utils/sse'; import { initSSE, closeSSE } from '@/utils/sse'; // 引入 closeSSE
const bgColorStore = useBgColor(); const bgColorStore = useBgColor();
// ========================================== // ======================缩放逻辑======================
// ✅ 第2步:缩放逻辑(从旧 script 移过来)
// ==========================================
const scale = ref(1); const scale = ref(1);
const DESIGN_WIDTH = 1920; const DESIGN_WIDTH = 1920;
@@ -44,17 +37,14 @@ const calcScale = () => {
scale.value = ratio; scale.value = ratio;
}; };
let timer: any = null; let timer: number | null = null;
const resize = () => { const resize = () => {
clearTimeout(timer); clearTimeout(timer);
timer = setTimeout(calcScale, 100); timer = window.setTimeout(calcScale, 100);
}; };
// ========================================== // ======================生命周期======================
// ✅ 第3步:生命周期钩子(统一写在这里)
// ==========================================
onMounted(() => { onMounted(() => {
// 缩放初始化
calcScale(); calcScale();
window.addEventListener('resize', resize); window.addEventListener('resize', resize);
@@ -68,24 +58,18 @@ onMounted(() => {
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('resize', resize); window.removeEventListener('resize', resize);
clearTimeout(timer); if (timer) clearTimeout(timer);
closeWebSocket();
closeSSE();
}); });
// ========================================== // ======================原有业务逻辑======================
// ✅ 第4步:原有业务逻辑
// ==========================================
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
const theme = computed(() => settingsStore.theme); const theme = computed(() => settingsStore.theme);
const sidebar = computed(() => useAppStore().sidebar); const sidebar = computed(() => useAppStore().sidebar);
const device = computed(() => useAppStore().device); const device = computed(() => useAppStore().device);
const needTagsView = computed(() => settingsStore.tagsView); const needTagsView = computed(() => settingsStore.tagsView);
// const fixedHeader = computed(() => settingsStore.fixedHeader);
// const layout = computed(() => settingsStore.navType);
// const showSidebar = computed(() => {
// if (sidebar.value.hide) return false;
// return layout.value === NavTypeEnum.LEFT || layout.value === NavTypeEnum.MIX;
// });
const classObj = computed(() => ({ const classObj = computed(() => ({
hideSidebar: !sidebar.value.opened, hideSidebar: !sidebar.value.opened,
@@ -112,10 +96,6 @@ watchEffect(() => {
const navbarRef = ref<InstanceType<typeof Navbar>>(); const navbarRef = ref<InstanceType<typeof Navbar>>();
const settingRef = ref<InstanceType<typeof Settings>>(); const settingRef = ref<InstanceType<typeof Settings>>();
// const handleClickOutside = () => {
// useAppStore().closeSideBar({ withoutAnimation: false });
// };
const setLayout = () => { const setLayout = () => {
settingRef.value?.openSetting(); settingRef.value?.openSetting();
}; };

View File

@@ -17,13 +17,12 @@ const whiteList = ['/login', '/register', '/social-callback', '/register*', '/re
// 全局标记:动态路由是否已经添加过(只加载一次!) // 全局标记:动态路由是否已经添加过(只加载一次!)
let isDynamicRoutesAdded = false; let isDynamicRoutesAdded = false;
// ✅ 新增:导航锁,防止多个路由导航并发执行
let isNavigating = false;
// 判断是否在白名单
const isWhiteList = (path: string) => {
return whiteList.some((pattern) => isPathMatch(pattern, path));
};
// ✅ 关键修改:记录当前已添加的动态路由名称列表 // ✅ 关键修改:记录当前已添加的动态路由名称列表
let currentDynamicRouteNames: string[] = []; let currentDynamicRouteNames: string[] = [];
// ✅ 关键修改:清除所有已记录的动态路由 // ✅ 关键修改:清除所有已记录的动态路由
const removeDynamicRoutes = () => { const removeDynamicRoutes = () => {
currentDynamicRouteNames.forEach((name) => { currentDynamicRouteNames.forEach((name) => {
@@ -31,23 +30,35 @@ const removeDynamicRoutes = () => {
router.removeRoute(name); router.removeRoute(name);
} }
}); });
// 清空记录数组
currentDynamicRouteNames = []; currentDynamicRouteNames = [];
}; };
router.beforeEach(async (to, from) => {
NProgress.start();
// 判断是否在白名单
const isWhiteList = (path: string) => {
return whiteList.some((pattern) => isPathMatch(pattern, path));
};
router.beforeEach(async (to, from) => {
// ========== 导航锁核心 ==========
// 如果正在导航,直接终止本次跳转,防止并发多个守卫
if (isNavigating) {
NProgress.done();
return false;
}
isNavigating = true;
NProgress.start();
const hasToken = getToken(); const hasToken = getToken();
const userStore = useUserStore(); const userStore = useUserStore();
const permissionStore = usePermissionStore(); const permissionStore = usePermissionStore();
try {
if (hasToken) { if (hasToken) {
// 设置标题 // 设置标题
to.meta.title && useSettingsStore().setTitle(to.meta.title as string); to.meta.title && useSettingsStore().setTitle(to.meta.title as string);
// 已登录 且 去登录页 → 跳首页 // 已登录 且 去登录页 → 跳首页
if (to.path === '/login') { if (to.path === '/login') {
NProgress.done();
return '/'; return '/';
} }
@@ -55,6 +66,8 @@ router.beforeEach(async (to, from) => {
if (isWhiteList(to.path)) { if (isWhiteList(to.path)) {
return; return;
} }
// roles为空,需要拉取用户信息+构建动态路由(只执行一次)
if (userStore.roles.length === 0) { if (userStore.roles.length === 0) {
isRelogin.show = true; isRelogin.show = true;
removeDynamicRoutes(); removeDynamicRoutes();
@@ -67,9 +80,9 @@ router.beforeEach(async (to, from) => {
isDynamicRoutesAdded = false; isDynamicRoutesAdded = false;
return '/'; return '/';
} }
isRelogin.show = false; isRelogin.show = false;
permissionStore.resetRoutes(); permissionStore.resetRoutes();
// 生成动态路由 // 生成动态路由
const accessRoutes = await permissionStore.generateRoutes(); const accessRoutes = await permissionStore.generateRoutes();
// 添加路由(避免重复添加) // 添加路由(避免重复添加)
@@ -85,31 +98,43 @@ router.beforeEach(async (to, from) => {
} }
} }
}); });
// ✅ 标记:路由已添加
isDynamicRoutesAdded = true; isDynamicRoutesAdded = true;
// 管理员登录强制跳转 // 登录页跳转进来,直接跳首页;其他情况重新触发导航加载动态路由
if (from.path === '/login') { if (from.path === '/login') {
return { path: '/community/communitylist', replace: true }; return '/community/communitylist';
} else { } else {
// 重新进入当前路由(让新添加的路由生效) // 这里replace仅在首次加载路由时执行;后续菜单切换不会走到这里
return { ...to, replace: true }; return { ...to, replace: true };
} }
} }
// ✅ 已经有角色、动态路由已经构建完成:直接放行菜单切换,不会再走上面的动态路由构建逻辑
return; return;
} else { } else {
// 未登录 // 未登录
isDynamicRoutesAdded = false; isDynamicRoutesAdded = false;
if (isWhiteList(to.path)) { if (isWhiteList(to.path)) {
return; return;
} else { } else {
NProgress.done();
return `/login`; return `/login`;
} }
} }
} catch (err) {
console.error('路由守卫异常:', err);
return false;
} finally {
// ✅ 无论成功失败,必须释放导航锁
isNavigating = false;
}
}); });
router.afterEach(() => { router.afterEach(() => {
NProgress.done(); NProgress.done();
}); });
// ✅ 导航发生错误的时候,也要释放锁(路由跳转报错时afterEach不一定执行)
router.onError(() => {
isNavigating = false;
NProgress.done();
});

View File

@@ -205,7 +205,6 @@ export const useHouseStore = defineStore(
floorslist.value.add(floor.floorNo); floorslist.value.add(floor.floorNo);
}); });
}); });
console.log(units);
return; return;
}); });
}; };
@@ -253,7 +252,6 @@ export const useHouseStore = defineStore(
treedata.value = []; treedata.value = [];
const res = await getVillageTree(villageid); const res = await getVillageTree(villageid);
treedata.value = convertBuildingToTree(res.data.buildings || []); treedata.value = convertBuildingToTree(res.data.buildings || []);
buildHousePathCache(treedata.value); buildHousePathCache(treedata.value);
return treedata.value; return treedata.value;
} catch (err) { } catch (err) {

View File

@@ -31,7 +31,7 @@ const reg = {
number: /^[0-9]+$/, number: /^[0-9]+$/,
// 完整版:所有emoji+肤色修饰+组合连接符+全部颜文字符号 // 完整版:所有emoji+肤色修饰+组合连接符+全部颜文字符号
fullFilterReg: 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 /[\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}。「」、・~ー『』「」・ヽヾゝゞ〆゛゜○●△▲□■◇◆◎▽▼┐┌└┘╮╭╰╯┬┴┼│─╱╲╳︵︶︹︺︿﹀︽︾﹁﹂﹃﹄ァ-ヺヽヾ]/u
}; };
// 自动推导 TS 类型(最标准) // 自动推导 TS 类型(最标准)
export type RegType = keyof typeof reg; export type RegType = keyof typeof reg;

66
src/utils/html.ts Normal file
View File

@@ -0,0 +1,66 @@
/**
* 剥离 HTML 标签,提取纯文本
* - 自动将 <img>/<video>/<audio> 替换为 [图片]/[视频]/[音频] 占位
* - 自动解码 &nbsp; &amp; 等 HTML entity
* - 自动合并多余空白
* - 可按需截断
*/
export function stripHtml(html: string | null | undefined, maxLen?: number): string {
if (!html) return '';
let text: string = html;
// 1. 媒体标签替换为占位符(保留语义提示)
text = text
.replace(/<img[^>]*>/gi, '[图片]')
.replace(/<video[^>]*>.*?<\/video>/gi, '[视频]')
.replace(/<audio[^>]*>.*?<\/audio>/gi, '[音频]')
.replace(/<iframe[^>]*>.*?<\/iframe>/gi, '[嵌入内容]');
// 2. 剥离所有剩余 HTML 标签(<p> <br> <div> ...)
text = text.replace(/<[^>]+>/g, '');
// 3. HTML entity 解码
text = decodeHtmlEntity(text);
// 4. 合并多余空白(多个空格/换行 → 单个空格)
text = text.replace(/\s+/g, ' ').trim();
// 5. 截断
if (maxLen && text.length > maxLen) {
text = text.slice(0, maxLen) + '...';
}
return text;
}
/**
* 解码 HTML 实体
* 浏览器环境用 DOMParser,非浏览器环境用正则 fallback
*/
function decodeHtmlEntity(str: string): string {
if (typeof DOMParser === 'undefined') {
const entityMap: Record<string, string> = {
'&nbsp;': ' ',
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'",
'&#x27;': "'",
'&#x2F;': '/',
'&copy;': '©',
'&reg;': '®'
};
return str.replace(/&[a-z]+;|&#\d+;|&#x[0-9a-f]+;/gi, (match) => {
if (entityMap[match]) return entityMap[match];
const num = match.match(/&#(\d+);/)?.[1];
if (num) return String.fromCharCode(Number(num));
const hex = match.match(/&#x([0-9a-f]+);/i)?.[1];
if (hex) return String.fromCharCode(parseInt(hex, 16));
return match;
});
}
const doc = new DOMParser().parseFromString(str, 'text/html');
return doc.documentElement.textContent ?? '';
}

View File

@@ -59,14 +59,14 @@ export function printVueDiv(elementId: string, title: string = '打印') {
table { table {
border-collapse: collapse; border-collapse: collapse;
width: 100% !important; width: 100% !important;
table-layout: fixed; /* 固定表格布局,防止列变形 */ /*table-layout: fixed; !* 固定表格布局,防止列变形 *!*/
} }
table td, table th { table td, table th {
border: 1px solid #333; border: 1px solid #333;
font-size: 9px; /* 表格字体最小化 */ font-size: 9px; /* 表格字体最小化 */
text-align: center; text-align: center;
white-space: nowrap; /* ✅ 禁止文字自动换行(核心) */ /*white-space: nowrap; !* ✅ 禁止文字自动换行(核心) *!*/
overflow: hidden; /*overflow: hidden;*/
} }
/* 适配合并列 */ /* 适配合并列 */
td[colspan], th[colspan] { td[colspan], th[colspan] {
@@ -85,7 +85,7 @@ export function printVueDiv(elementId: string, title: string = '打印') {
line-height: 14px; line-height: 14px;
color: rgba(48, 49, 51, 1); color: rgba(48, 49, 51, 1);
font-size: 20px; font-size: 20px;
margin-bottom: 50px; margin-bottom: 20px;
} }
p { p {
line-height: 14px; line-height: 14px;
@@ -105,23 +105,27 @@ export function printVueDiv(elementId: string, title: string = '打印') {
td { td {
border: 1px solid #dddddd; border: 1px solid #dddddd;
height: 50px; height: 50px;
line-height: 45px; line-height: normal;
background-color: rgba(255, 255, 255, 1); background-color: rgba(255, 255, 255, 1);
text-align: center; text-align: center;
font-size: 16px;
font-weight: 450; font-weight: 450;
} }
.footertr td { .footertr td {
height: 90px; height: 60px;
} }
.foorter { .foorter {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
width: 85%; width: 100%;
margin: 0 auto;
gap: 30px; gap: 30px;
margin-top: 30px; margin: 20px auto 0;
}
.formark {
font-size: smaller;
margin-top: 10px;
} }
</style> </style>
`; `;

View File

@@ -52,7 +52,10 @@ const whiteVillageId = [
'/paidInExpenses/*', '/paidInExpenses/*',
// 租赁管理明细 // 租赁管理明细
'/leaseManagementDetail/*', '/leaseManagementDetail/*',
'/warehouse/*' '/warehouse/*',
// 电表启停
'/electricityDevice/controlSwitch'
// '/building/list' // '/building/list'
]; ];

View File

@@ -1,15 +1,40 @@
import { getToken } from '@/utils/auth'; import { getToken } from '@/utils/auth';
import { ElNotification } from 'element-plus'; import { ElNotification } from 'element-plus';
import { useNoticeStore } from '@/store/modules/notice'; import { useNoticeStore } from '@/store/modules/notice';
import { useEventSource } from '@vueuse/core';
import { watch } from 'vue';
// 初始化 // 全局单例保存SSE实例,防止多次创建
export const initSSE = (url: any) => { let eventSourceInstance: ReturnType<typeof useEventSource> | null = null;
let stopWatchError: (() => void) | null = null;
let stopWatchData: (() => void) | null = null;
// 关闭SSE、清理监听
export const closeSSE = () => {
if (eventSourceInstance) {
eventSourceInstance.close();
eventSourceInstance = null;
}
// 销毁watch
stopWatchError?.();
stopWatchData?.();
stopWatchError = null;
stopWatchData = null;
};
// 初始化SSE
export const initSSE = (url: string) => {
if (import.meta.env.VITE_APP_SSE === 'false') { if (import.meta.env.VITE_APP_SSE === 'false') {
return; return;
} }
// 关键:新建之前先关闭旧连接!
closeSSE();
url = url + '?Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID; const token = getToken();
const { data, error } = useEventSource(url, [], { const clientId = import.meta.env.VITE_APP_CLIENT_ID;
const sseUrl = url + '?Authorization=Bearer ' + token + '&clientid=' + clientId;
eventSourceInstance = useEventSource(sseUrl, [], {
autoReconnect: { autoReconnect: {
retries: 5, retries: 5,
delay: 5000, delay: 5000,
@@ -19,25 +44,69 @@ export const initSSE = (url: any) => {
} }
}); });
watch(error, () => { const { data, error } = eventSourceInstance;
stopWatchError = watch(error, () => {
console.log('SSE connection error:', error.value); console.log('SSE connection error:', error.value);
error.value = null; // error.value = null; // 不要手动覆盖,会干扰useEventSource内部状态
}); });
watch(data, () => { stopWatchData = watch(data, () => {
if (!data.value) return; if (!data.value) return;
const notice = JSON.parse(data.value); const notice = tryParseJson(data.value as string);
if (typeof notice === 'string') {
useNoticeStore().addNotice({ useNoticeStore().addNotice({
message: notice.message, message: notice,
read: false, read: false,
time: new Date().toLocaleString() time: new Date().toLocaleString()
}); });
ElNotification({ ElNotification({
title: notice.title, title: '消息',
message: notice.message, message: notice,
type: notice.type, type: 'success',
duration: 3000 duration: 3000
}); });
data.value = null; data.value = null;
return;
} else {
const title = typeof notice.title === 'string' ? notice.title : '消息';
const message = typeof notice.message === 'string' ? notice.message : '';
const type = typeof notice.type === 'string' ? notice.type : 'info';
useNoticeStore().addNotice({
message,
read: false,
time: new Date().toLocaleString()
});
ElNotification({
title,
message,
type: type as 'success' | 'warning' | 'info' | 'error',
duration: 3000
});
data.value = null;
}
}); });
}; };
/**
* 尝试解析后端返回的字符串
* 如果是合法JSON对象 → 返回 Record<string, unknown>
* 否则(数组/null/基础值/非JSON字符串) → 返回原字符串
* @param str 后端传过来的字符串
*/
function tryParseJson(str: string): string | Record<string, unknown> {
if (typeof str !== 'string' || str === '') return str;
const trimStr = str.trim();
if (trimStr === '') return str;
try {
const parsed = JSON.parse(trimStr);
// 只有纯对象才返回对象,数组/null/基础值等统一当作字符串处理
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
return parsed as Record<string, unknown>;
}
return str;
} catch {
return str;
}
}

View File

@@ -1,18 +1,37 @@
import { getToken } from '@/utils/auth'; import { getToken } from '@/utils/auth';
import { ElNotification } from 'element-plus'; import { ElNotification } from 'element-plus';
import { useNoticeStore } from '@/store/modules/notice'; import { useNoticeStore } from '@/store/modules/notice';
import { useWebSocket } from '@vueuse/core';
// 全局保存 ws 实例,单例,防止重复创建
let wsInstance: ReturnType<typeof useWebSocket> | null = null;
// 对外:关闭 websocket
export const closeWebSocket = () => {
if (wsInstance) {
wsInstance.close();
wsInstance = null;
console.log('websocket 已手动关闭');
}
};
// 初始化socket // 初始化socket
export const initWebSocket = (url: any) => { export const initWebSocket = (url: string) => {
if (import.meta.env.VITE_APP_WEBSOCKET === 'false') { if (import.meta.env.VITE_APP_WEBSOCKET === 'false') {
return; return;
} }
url = url + '?Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID; // 已经存在实例,先关闭旧的,避免多连接
useWebSocket(url, { if (wsInstance) {
closeWebSocket();
}
const token = getToken();
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
const wsUrl = url + '?Authorization=Bearer ' + token + '&clientid=' + clientId;
wsInstance = useWebSocket(wsUrl, {
autoReconnect: { autoReconnect: {
// 重连最大次数
retries: 3, retries: 3,
// 重连间隔
delay: 1000, delay: 1000,
onFailed() { onFailed() {
console.log('websocket重连失败'); console.log('websocket重连失败');
@@ -20,9 +39,7 @@ export const initWebSocket = (url: any) => {
}, },
heartbeat: { heartbeat: {
message: JSON.stringify({ type: 'ping' }), message: JSON.stringify({ type: 'ping' }),
// 发送心跳的间隔
interval: 10000, interval: 10000,
// 接收到心跳response的超时时间
pongTimeout: 2000 pongTimeout: 2000
}, },
onConnected() { onConnected() {
@@ -32,20 +49,53 @@ export const initWebSocket = (url: any) => {
console.log('websocket已经断开'); console.log('websocket已经断开');
}, },
onMessage: (_, e) => { onMessage: (_, e) => {
if (e.data.indexOf('ping') > 0) { // 心跳pong消息直接跳过
if (e.data.indexOf('ping') > 0 || e.data.indexOf('pong') > 0) {
return; return;
} }
const msg = tryParseJson(e.data);
let messageText = '';
let title = '消息';
let type: 'success' | 'warning' | 'info' | 'error' = 'success';
if (typeof msg === 'string') {
messageText = msg;
} else {
title = typeof msg.title === 'string' ? msg.title : '消息';
messageText = typeof msg.message === 'string' ? msg.message : '';
type = (typeof msg.type === 'string' ? msg.type : 'info') as typeof type;
}
useNoticeStore().addNotice({ useNoticeStore().addNotice({
message: e.data, message: messageText,
read: false, read: false,
time: new Date().toLocaleString() time: new Date().toLocaleString()
}); });
ElNotification({ ElNotification({
title: '消息', title,
message: e.data, message: messageText,
type: 'success', type,
duration: 3000 duration: 3000
}); });
} }
}); });
}; };
/**
* 和sse共用的解析函数:
* JSON对象返回Record,其他返回原始字符串
*/
function tryParseJson(str: string): string | Record<string, unknown> {
if (typeof str !== 'string' || str === '') return str;
const trimStr = str.trim();
if (trimStr === '') return str;
try {
const parsed = JSON.parse(trimStr);
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
return parsed as Record<string, unknown>;
}
return str;
} catch {
return str;
}
}

View File

@@ -18,10 +18,22 @@ export type ColumnConfig = {
*/ */
export type SpanMethodCallback = (params: { row: any; rowIndex: number; columnIndex: number }) => { rowspan: number; colspan: number }; export type SpanMethodCallback = (params: { row: any; rowIndex: number; columnIndex: number }) => { rowspan: number; colspan: number };
/**
* 行样式回调函数类型
* 返回的样式会 merge 到该行所有单元格上
*/
export type RowStyleCallback = (params: { row: any; rowIndex: number }) => Partial<XLSX.CellStyle> | undefined;
/** /**
* 前端导出Excel(支持多级表头) * 前端导出Excel(支持多级表头)
*/ */
export function exportToExcel(data: any[], columns: ColumnConfig[], filename: string, spanMethod?: SpanMethodCallback) { export function exportToExcel(
data: any[],
columns: ColumnConfig[],
filename: string,
spanMethod?: SpanMethodCallback,
rowStyleCallback?: RowStyleCallback
) {
if (!data || data.length === 0) { if (!data || data.length === 0) {
ElMessage.warning('没有可导出的数据'); ElMessage.warning('没有可导出的数据');
return; return;
@@ -80,27 +92,35 @@ export function exportToExcel(data: any[], columns: ColumnConfig[], filename: st
worksheet[cellAddress] = { t: 's', v: '' }; worksheet[cellAddress] = { t: 's', v: '' };
} }
const isHeader = R < headerRows; const isHeader = R < headerRows;
const dataRowIndex = R - headerRows;
// 用户自定义的行样式(仅对数据行生效)
const customRowStyle =
!isHeader && rowStyleCallback && dataRowIndex >= 0 ? rowStyleCallback({ row: data[dataRowIndex], rowIndex: dataRowIndex }) : undefined;
worksheet[cellAddress].s = { worksheet[cellAddress].s = {
fill: isHeader fill:
customRowStyle?.fill ??
(isHeader
? { ? {
fgColor: { rgb: 'bec4cb' }, fgColor: { rgb: 'bec4cb' },
patternType: 'solid' patternType: 'solid'
} }
: undefined, : undefined),
font: isHeader font:
customRowStyle?.font ??
(isHeader
? { ? {
color: { rgb: 'FFFFFF' }, color: { rgb: 'FFFFFF' },
bold: true, bold: true,
sz: 11 sz: 11
} }
: undefined, : undefined),
border: { border: customRowStyle?.border ?? {
top: { style: 'thin', color: { rgb: '000000' } }, top: { style: 'thin', color: { rgb: '000000' } },
bottom: { style: 'thin', color: { rgb: '000000' } }, bottom: { style: 'thin', color: { rgb: '000000' } },
left: { style: 'thin', color: { rgb: '000000' } }, left: { style: 'thin', color: { rgb: '000000' } },
right: { style: 'thin', color: { rgb: '000000' } } right: { style: 'thin', color: { rgb: '000000' } }
}, },
alignment: { alignment: customRowStyle?.alignment ?? {
horizontal: 'center', horizontal: 'center',
vertical: 'center' vertical: 'center'
} }

View File

@@ -24,11 +24,11 @@
<el-form-item label="建成时间" prop="buildingArea"> <el-form-item label="建成时间" prop="buildingArea">
<el-date-picker v-model="builtTime" placeholder="选择建成时间" type="date" value-format="YYYY-MM-DD" @change="changeBuiltTime" /> <el-date-picker v-model="builtTime" placeholder="选择建成时间" type="date" value-format="YYYY-MM-DD" @change="changeBuiltTime" />
</el-form-item> </el-form-item>
<el-form-item label="房屋用途" prop="buildingArea"> <!-- <el-form-item label="房屋用途" prop="houseUse">-->
<el-select v-model="form.houseUse" placeholder="请选择房屋用途" style="width: 240px"> <!-- <el-select v-model="form.houseUse" placeholder="请选择房屋用途" style="width: 240px">-->
<el-option v-for="item in houseUserList" :key="item.value" :label="item.label" :value="item.value" /> <!-- <el-option v-for="item in houseUserList" :key="item.id" :label="item.name" :value="item.id" />-->
</el-select> <!-- </el-select>-->
</el-form-item> <!-- </el-form-item>-->
<el-form-item label="小区面积" prop="villageArea"> <el-form-item label="小区面积" prop="villageArea">
<el-input v-model.trim="form.villageArea" maxlength="8" placeholder="请输入小区面积"><template #append>㎡</template> </el-input> <el-input v-model.trim="form.villageArea" maxlength="8" placeholder="请输入小区面积"><template #append>㎡</template> </el-input>
</el-form-item> </el-form-item>
@@ -106,6 +106,7 @@ import { CascaderValue } from 'element-plus';
import { addVillageAPI, editVillageAPI, getVillageByIdAPI, uploadVillageImageAPI } from '@/api/system/community'; import { addVillageAPI, editVillageAPI, getVillageByIdAPI, uploadVillageImageAPI } from '@/api/system/community';
import { communitylist_rows_type } from '@/api/system/community/type'; import { communitylist_rows_type } from '@/api/system/community/type';
import { Delete, Plus } from '@element-plus/icons-vue'; import { Delete, Plus } from '@element-plus/icons-vue';
import { listHouseUseType_add_house } from '@/api/system/houseUse';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@@ -116,24 +117,7 @@ const uploadFileUrl = `${apiPrefix}/village/uploadImages`;
// 获取省 市 区╬╬╬╬╬╬╬ // 获取省 市 区╬╬╬╬╬╬╬
const pcaTextArrList = ref<DataItem[]>(pcaTextArr); const pcaTextArrList = ref<DataItem[]>(pcaTextArr);
// 获取小区用途 // 获取小区用途
const houseUserList = ref([ const houseUserList = ref([]);
{
value: 0,
label: '住宅'
},
{
value: 1,
label: '商住两用'
},
{
value: 2,
label: '商业'
},
{
value: 3,
label: '办公'
}
]);
const formRef = ref(); const formRef = ref();
const uploadRef = ref(); const uploadRef = ref();
@@ -420,7 +404,6 @@ const submitForm = () => {
// 3) 有新文件:先上传新文件,再与已有URL合并 // 3) 有新文件:先上传新文件,再与已有URL合并
const formdata = new FormData(); const formdata = new FormData();
newFiles.forEach((file) => formdata.append('files', file)); newFiles.forEach((file) => formdata.append('files', file));
console.log('newFiles', newFiles);
uploadVillageImageAPI(formdata).then((res) => { uploadVillageImageAPI(formdata).then((res) => {
const uploadedUrls = res.data.success.map((x) => x.url).filter(Boolean); const uploadedUrls = res.data.success.map((x) => x.url).filter(Boolean);
// 4) 最终图片 = 已有图片 + 新上传图片(避免覆盖) // 4) 最终图片 = 已有图片 + 新上传图片(避免覆盖)
@@ -473,13 +456,21 @@ function submitFormInfo() {
const cancelForm = () => { const cancelForm = () => {
router.back(); router.back();
}; };
// 获取房屋用途
const getHouseUserList = () => {
listHouseUseType_add_house().then((res) => {
houseUserList.value = res.data;
});
};
getHouseUserList();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.box { .box {
background: #deedff; background: #deedff;
height: 100%; height: 100%;
overflow: hidden; overflow: auto;
.header { .header {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -21,33 +21,10 @@
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- </el-col>--> <!-- </el-col>-->
<el-col :span="11"> <el-col :span="11">
<el-form-item label="报名截止日期" prop="applyDeadline">
<el-date-picker
v-model="deadline"
:disabled-date="disabledDate"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择报名截止日期"
style="width: 100%"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
@update:model-value="
(data) => {
if (data) {
form.applyDeadline = data;
} else {
form.applyDeadline = '';
}
}
"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="13">
<el-form-item label="活动时间" prop="startTime"> <el-form-item label="活动时间" prop="startTime">
<el-date-picker <el-date-picker
v-model="activityTime" v-model="activityTime"
:disabled-date="disabledDate"
end-placeholder="结束活动" end-placeholder="结束活动"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
range-separator="至" range-separator="至"
@@ -68,6 +45,31 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<el-row>
<el-col :span="13">
<el-form-item label="报名截止日期" prop="applyDeadline">
<el-date-picker
v-model="deadline"
:disabled="!Boolean(form.startTime)"
:disabled-date="disabledDateDeadline"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择报名截止日期"
style="width: 100%"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
@update:model-value="
(data) => {
if (data) {
form.applyDeadline = data;
} else {
form.applyDeadline = '';
}
}
"
/>
</el-form-item>
</el-col>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="活动地点" prop="address"> <el-form-item label="活动地点" prop="address">
<el-input v-model.trim="form.address" maxlength="50" placeholder="请输入活动地点" show-word-limit type="textarea" /> <el-input v-model.trim="form.address" maxlength="50" placeholder="请输入活动地点" show-word-limit type="textarea" />
@@ -351,10 +353,15 @@ const disabledDate = (time: string) => {
const selectDate = new Date(time); const selectDate = new Date(time);
return selectDate.getTime() < today.getTime(); return selectDate.getTime() < today.getTime();
}; };
const disabledDateDeadline = (time: string) => {
const today = new Date(form.value.endTime);
const selectDate = new Date(time);
return selectDate.getTime() > today.getTime();
};
function disabledSignInDate(time: string) { function disabledSignInDate(time: string) {
const selectDate = new Date(time); const selectDate = new Date(time);
const today = new Date(form.value.endTime); const today = new Date(form.value.endTime);
return selectDate.getTime() > today.getTime(); return selectDate.getTime() > today.getTime() || selectDate.getTime() < new Date(new Date().toLocaleDateString()).getTime();
} }
const userid = ref(); const userid = ref();
// 报名截止日期 // 报名截止日期

View File

@@ -4,9 +4,9 @@
<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" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" @submit.prevent="handleQuery">
<el-form-item label="标题" prop="title"> <el-form-item label="标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入标题" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.title" clearable placeholder="请输入标题" @keyup.enter="handleQuery" />
</el-form-item> </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>
@@ -19,13 +19,13 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb10">
<right-toolbar <right-toolbar
:disable-delete="multiple" :disable-delete="multiple"
:disable-edit="single" :disable-edit="single"
:show-add="checkPermi(['activity:activity:add'])" :show-add="checkPermi(['activity:activity:add'])"
:show-edit="checkPermi(['activity:activity:edit'])"
:show-delete="checkPermi(['activity:activity:remove'])" :show-delete="checkPermi(['activity:activity:remove'])"
:show-edit="checkPermi(['activity:activity:edit'])"
@update:add="handleAdd" @update:add="handleAdd"
@update:edit="handleUpdate()" @update:edit="handleUpdate()"
@update:delete="handleDelete()" @update:delete="handleDelete()"
@@ -33,65 +33,65 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="activityList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="activityList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="封面" align="center"> <el-table-column align="center" label="封面" min-width="130px">
<template #default="scope"> <template #default="scope">
<el-image <el-image
preview-teleported
lazy
:preview-src-list="[scope.row.coverImage]"
v-if="scope.row.coverImage" v-if="scope.row.coverImage"
:preview-src-list="[scope.row.coverImage]"
:src="scope.row.coverImage" :src="scope.row.coverImage"
fit="cover" fit="cover"
lazy
preview-teleported
style="width: 100px; height: 100px" style="width: 100px; height: 100px"
></el-image> ></el-image>
<span v-else style="color: grey">暂无</span> <span v-else style="color: grey">暂无</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="标题" align="center" prop="title" /> <el-table-column align="center" label="标题" prop="title" />
<el-table-column label="主办方" align="center" prop="organizer" /> <el-table-column align="center" label="主办方" prop="organizer" />
<el-table-column label="签到时间" width="120" align="center" prop="signInTime"> <el-table-column align="center" label="签到时间" prop="signInTime" width="120">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.signinStartTime }}-{{ scope.row.signinEndTime }}</span> <span>{{ scope.row.signinStartTime }}-{{ scope.row.signinEndTime }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="发布状态" width="100" align="center" prop="status"> <el-table-column align="center" label="发布状态" prop="status" width="100">
<template #default="scope"> <template #default="scope">
<DictTag :value="scope.row.status" :options="com_publish_status"></DictTag> <DictTag :options="com_publish_status" :value="scope.row.status"></DictTag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="活动时间" align="center"> <el-table-column align="center" label="活动时间">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.startTime }}</span> <span>{{ scope.row.startTime }}</span>
<span> -- </span> <span> -- </span>
<span>{{ scope.row.endTime }}</span> <span>{{ scope.row.endTime }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="活动地点" align="center" prop="address" /> <el-table-column align="center" label="活动地点" prop="address" />
<el-table-column label="联系人" width="120" align="center" prop="contactName" /> <el-table-column align="center" label="联系人" prop="contactName" width="120" />
<el-table-column label="联系电话" width="120" align="center" prop="contactPhone" /> <el-table-column align="center" label="联系电话" prop="contactPhone" width="120" />
<el-table-column label="活动名额" width="80" align="center" prop="contactPhone"> <el-table-column align="center" label="活动名额" prop="contactPhone" width="80">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.quota === 0">无限制</span> <span v-if="scope.row.quota === 0">无限制</span>
<span v-else>{{ scope.row.quota }}人</span> <span v-else>{{ scope.row.quota }}人</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="200px" fixed="right"> <el-table-column align="center" fixed="right" label="操作" width="200px">
<template #default="scope"> <template #default="scope">
<el-button v-if="scope.row.status === '0'" link type="primary" @click="handleSend(scope.row)">发布</el-button> <el-button v-if="scope.row.status === '0'" link type="primary" @click="handleSend(scope.row)">发布</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['activity:activity:edit']">编辑</el-button> <el-button v-has-permi="['activity:activity:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['activity:activity:remove']">删除</el-button> <el-button v-has-permi="['activity:activity:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" append-to-body width="500px">
<el-form ref="activityFormRef" :model="form" :rules="rules" label-width="120px"> <el-form ref="activityFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="发布人姓名" prop="createName"> <el-form-item label="发布人姓名" prop="createName">
<el-input maxlength="20" v-model.trim="form.createName" placeholder="请输入发布人姓名" /> <el-input v-model.trim="form.createName" maxlength="20" placeholder="请输入发布人姓名" />
</el-form-item> </el-form-item>
<el-form-item label="发布后状态" prop="status"> <el-form-item label="发布后状态" prop="status">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
@@ -109,7 +109,7 @@
</div> </div>
</template> </template>
<script setup name="Activity" lang="ts"> <script lang="ts" name="Activity" setup>
import { listActivity, delActivity, updateActivity } from '@/api/system/Activity'; import { listActivity, delActivity, updateActivity } from '@/api/system/Activity';
import { ActivityVO, ActivityQuery, ActivityForm } from '@/api/system/Activity/type'; import { ActivityVO, ActivityQuery, ActivityForm } from '@/api/system/Activity/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';

View File

@@ -93,7 +93,7 @@
<el-input v-model.trim="form.handleContent" :rows="5" maxlength="200" show-word-limit type="textarea"></el-input> <el-input v-model.trim="form.handleContent" :rows="5" maxlength="200" show-word-limit type="textarea"></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button v-if="form.status !== '0'" type="primary" @click="submitForm">提交</el-button> <el-button v-if="formInfo.status !== '1'" type="primary" @click="submitForm">提交</el-button>
<el-button @click="handleBack">返回</el-button> <el-button @click="handleBack">返回</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>

View File

@@ -4,14 +4,14 @@
<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="90px" label-position="left" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" label-position="left" label-width="90px">
<el-form-item label="投诉类型" prop="typeId"> <el-form-item label="投诉状态" prop="status">
<el-select @clear="handleClear" clearable v-model="queryParams.typeId" placeholder="请选择投诉类型" style="width: 240px"> <el-select v-model="queryParams.status" clearable placeholder="请选择投诉状态" style="width: 240px" @clear="handleClear">
<el-option :disabled="item.status === '1'" v-for="item in complainTypeList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in com_complaint_status" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="投诉人姓名" prop="complaintName"> <el-form-item label="投诉人姓名" prop="complaintName">
<el-input v-model="queryParams.complaintName" placeholder="请输入投诉人姓名" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.complaintName" clearable placeholder="请输入投诉人姓名" @keyup.enter="handleQuery" />
</el-form-item> </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>
@@ -22,31 +22,31 @@
</div> </div>
</transition> </transition>
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<el-table v-loading="loading" border :data="complaintList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="complaintList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="投诉类型" align="center" prop="typeName" /> <el-table-column align="center" label="投诉类型" prop="typeName" />
<el-table-column label="问题描述" show-overflow-tooltip align="center" prop="problemDes" /> <el-table-column align="center" label="问题描述" prop="problemDes" show-overflow-tooltip />
<el-table-column label="投诉人姓名" align="center" prop="complaintName" /> <el-table-column align="center" label="投诉人姓名" prop="complaintName" />
<el-table-column label="手机号" align="center" prop="phone" /> <el-table-column align="center" label="手机号" prop="phone" />
<el-table-column label="投诉时间" align="center" prop="createTime" /> <el-table-column align="center" label="投诉时间" prop="createTime" />
<el-table-column label="投诉状态" align="center" prop="status"> <el-table-column align="center" label="投诉状态" prop="status">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_complaint_status" :value="scope.row.status"></dict-tag> <dict-tag :options="com_complaint_status" :value="scope.row.status"></dict-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="处理人" align="center" prop="handleName" /> <el-table-column align="center" label="处理人" prop="handleName" />
<el-table-column label="处理描述" show-overflow-tooltip align="center" prop="handleContent" /> <el-table-column align="center" label="处理描述" prop="handleContent" show-overflow-tooltip />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['complaint:complaint:query']">查看详情</el-button> <el-button v-has-permi="['complaint:complaint:query']" link type="primary" @click="handleUpdate(scope.row)">查看详情</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="Complaint" lang="ts"> <script lang="ts" name="Complaint" setup>
import { listComplaint } from '@/api/system/Complaint'; import { listComplaint } from '@/api/system/Complaint';
import { ComplaintVO, ComplaintQuery, ComplaintForm } from '@/api/system/Complaint/type'; import { ComplaintVO, ComplaintQuery, ComplaintForm } from '@/api/system/Complaint/type';
import { listComplaintType } from '@/api/system/ComplainType'; import { listComplaintType } from '@/api/system/ComplainType';
@@ -120,9 +120,9 @@ const handleQuery = () => {
getList(); getList();
}; };
/** 清空投诉类型 */ /** 清空投诉状态 */
const handleClear = () => { const handleClear = () => {
queryParams.value.typeId = undefined; queryParams.value.status = undefined;
handleQuery(); handleQuery();
}; };
/** 重置按钮操作 */ /** 重置按钮操作 */

View File

@@ -46,12 +46,17 @@
></el-switch> ></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="创建时间" prop="createTime" width="180"> <el-table-column align="center" label="添加人" prop="createName" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span> <span>{{ scope.row.createName }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作"> <el-table-column align="center" label="创建时间" prop="createTime" width="180">
<template #default="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column align="center" class-name="small-padding fixed-width" label="操作">
<template #default="scope"> <template #default="scope">
<el-button v-has-permi="['complaintType:complaintType:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button> <el-button v-has-permi="['complaintType:complaintType:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button v-has-permi="['complaintType:complaintType:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button> <el-button v-has-permi="['complaintType:complaintType:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>

View File

@@ -147,7 +147,7 @@ function handleChange(val: (string | number)[]) {
} }
} }
// ! 投诉 ==================================================================================================== // ! 投诉 蚱蝉====================================================================================================
function getTree() { function getTree() {
houseStore.getCurrentVilageTreeHouse().then((res) => { houseStore.getCurrentVilageTreeHouse().then((res) => {
treedata.value = res; treedata.value = res;

View File

@@ -130,6 +130,8 @@ const form = ref({
}); });
const rules = { const rules = {
content: [{ required: true, message: '请输入办事内容', trigger: 'blur' }], content: [{ required: true, message: '请输入办事内容', trigger: 'blur' }],
handleTime: [{ required: true, message: '请输入办事时间', trigger: 'blur' }],
handlePosition: [{ required: true, message: '请输入办事地点', trigger: 'blur' }],
seekName: [ seekName: [
{ required: true, message: '请选择求助人', trigger: 'blur' }, { required: true, message: '请选择求助人', trigger: 'blur' },
{ {

View File

@@ -37,7 +37,9 @@
<el-table-column align="center" type="selection" width="55" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="办事内容" prop="content"> <el-table-column align="center" label="办事内容" prop="content">
<template #default="scope"> <template #default="scope">
<div class="color-blue moreRowEllipsis" v-html="scope.row.content"></div> <div class="color-blue moreRowEllipsis">
{{ stripHtml(scope.row.content) }}
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="求助人" prop="seekName" width="120" /> <el-table-column align="center" label="求助人" prop="seekName" width="120" />
@@ -48,7 +50,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="办事地点" prop="handlePosition" /> <el-table-column align="center" label="办事地点" prop="handlePosition" />
<el-table-column align="center" label="附属材料" prop="handlePosition" width="100"> <el-table-column align="center" label="附属材料" width="100">
<template #default="scope"> <template #default="scope">
<el-button v-if="scope.row.handleLogFileList?.length > 0" link type="primary" @click="openDialog(scope.row)">点击查看</el-button> <el-button v-if="scope.row.handleLogFileList?.length > 0" link type="primary" @click="openDialog(scope.row)">点击查看</el-button>
</template> </template>
@@ -86,6 +88,7 @@
import { listHandleLog, delHandleLog } from '@/api/system/HandleLog'; import { listHandleLog, delHandleLog } from '@/api/system/HandleLog';
import { HandleLogVO, HandleLogQuery, HandleLogForm, FileVO } from '@/api/system/HandleLog/type'; import { HandleLogVO, HandleLogQuery, HandleLogForm, FileVO } from '@/api/system/HandleLog/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
import { stripHtml } from '@/utils/html';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;

View File

@@ -33,6 +33,7 @@
maxlength="10" maxlength="10"
placeholder="请输入车牌号" placeholder="请输入车牌号"
style="text-transform: uppercase; letter-spacing: 2px" style="text-transform: uppercase; letter-spacing: 2px"
@input="handleInputCarNum"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -48,6 +49,7 @@
<el-form-item label="有效日期" prop="startTime"> <el-form-item label="有效日期" prop="startTime">
<el-date-picker <el-date-picker
v-model="checkDate" v-model="checkDate"
:disabled-date="disableDate"
end-placeholder="结束日期" end-placeholder="结束日期"
placeholder="选择日期" placeholder="选择日期"
range-separator="到" range-separator="到"
@@ -121,15 +123,22 @@ const form = ref({
endTime: '' // 结束日期 endTime: '' // 结束日期
}); });
const checkDate = ref([]); const checkDate = ref([]);
function handleDate() { // 禁用 过去日期
form.value.startTime = checkDate.value[0]; function disableDate(time: Date) {
form.value.endTime = checkDate.value[1]; return time.getTime() < new Date(new Date().toLocaleDateString()).getTime();
} }
// 处理日期选择
function handleDate(data: string[]) {
form.value.startTime = data[0] + ' 00:00:00';
form.value.endTime = data[1] + ' 23:59:59';
}
// 清空日期选择
function handleClear() { function handleClear() {
checkDate.value = []; checkDate.value = [];
form.value.startTime = ''; form.value.startTime = '';
form.value.endTime = ''; form.value.endTime = '';
} }
const rules = ref({ const rules = ref({
cardCode: [ cardCode: [
{ required: true, message: '请输入月卡编号', trigger: 'blur' }, { required: true, message: '请输入月卡编号', trigger: 'blur' },
@@ -150,8 +159,15 @@ const rules = ref({
handleName: [ handleName: [
{ required: false, message: '请输入姓名', trigger: 'blur' }, { required: false, message: '请输入姓名', trigger: 'blur' },
{ {
validator: (_: any, value: string | number) => validator(value, 'ChineseName'), validator: (_: any, value: string | number, callback: (error?: Error) => void) => {
message: '请输入正确的中文姓名', if (value === '' || value === null || value === undefined) {
return callback();
}
if (!validator(value, 'ChineseName')) {
return callback(new Error('请输入正确的中文姓名'));
}
return callback();
},
trigger: 'blur' trigger: 'blur'
} }
], ],
@@ -210,6 +226,12 @@ if (route.query.id) {
checkDate.value = [form.value.startTime, form.value.endTime]; checkDate.value = [form.value.startTime, form.value.endTime];
}); });
} }
// 处理 车牌号 输入 大写问题
function handleInputCarNum(val: string) {
form.value.carNum = val.toUpperCase();
}
// !== 提交 ============================================================================= // !== 提交 =============================================================================
// 提交 // 提交
const submitForm = () => { const submitForm = () => {

View File

@@ -45,7 +45,7 @@
<el-table-column align="center" label="绑定车牌号" prop="carNum" width="150" /> <el-table-column align="center" label="绑定车牌号" prop="carNum" width="150" />
<el-table-column align="center" label="剩余有效天数" prop="handleName" width="120"> <el-table-column align="center" label="剩余有效天数" prop="handleName" width="120">
<template #default="scope"> <template #default="scope">
{{ formatTimeDiff(scope.row.endTime) }} {{ handleDiffDays(scope.row.startTime, scope.row.endTime) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="支付方式" prop="payMethod" width="150"> <el-table-column align="center" label="支付方式" prop="payMethod" width="150">
@@ -118,7 +118,6 @@
import { listMonthCard, delMonthCard, addMonthCard, updateMonthCard } from '@/api/system/MonthCard'; import { listMonthCard, delMonthCard, addMonthCard, updateMonthCard } from '@/api/system/MonthCard';
import { MonthCardVO, MonthCardQuery, MonthCardForm } from '@/api/system/MonthCard/type'; import { MonthCardVO, MonthCardQuery, MonthCardForm } from '@/api/system/MonthCard/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
import { diffDays, formatTimeDiff } from '@/utils/time';
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'));
@@ -179,6 +178,32 @@ const data = reactive<PageData<MonthCardForm, MonthCardQuery>>({
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 一天对应的毫秒数 */
const ONE_DAY_MS = 1000 * 60 * 60 * 24;
/**
* 计算月卡剩余有效天数
* 规则:
* - endTime <= 当前时间 → 已过期,返回 0
* - startTime > endTime → 参数非法,返回 0
* - 同一天(如 9/2 00:00:00 ~ 9/2 23:59:59)视为 1 天
*/
function handleDiffDays(startTime: Date | string | number, endTime: Date | string | number): number {
const now = Date.now();
const start = new Date(startTime).getTime();
const end = new Date(endTime).getTime();
// 非法参数:开始时间晚于结束时间
if (start > end) return 0;
// 已过期
if (end <= now) return 0;
// 取「开始时间」和「当前时间」中较晚的那个作为计算起点
const effectiveStart = Math.max(start, now);
const diff = end - effectiveStart;
return Math.round(diff / ONE_DAY_MS);
}
/** 查询月卡管理列表 */ /** 查询月卡管理列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;

View File

@@ -93,7 +93,6 @@ import { formatTimeDiff } from '@/utils/time';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_parking_charge } = toRefs<any>(proxy?.useDict('com_parking_charge')); const { com_parking_charge } = toRefs<any>(proxy?.useDict('com_parking_charge'));
const { com_parking_payer_status } = toRefs<any>(proxy?.useDict('com_parking_payer_status')); const { com_parking_payer_status } = toRefs<any>(proxy?.useDict('com_parking_payer_status'));
const { com_pay_method } = toRefs<any>(proxy?.useDict('com_pay_method'));
const router = useRouter(); const router = useRouter();
interface ParkingCostType extends ParkingCostVO { interface ParkingCostType extends ParkingCostVO {

View File

@@ -61,8 +61,9 @@
<el-card v-else-if="activeTab == 'detail'"> <el-card v-else-if="activeTab == 'detail'">
<div> <div>
<el-table :data="list" style="width: 100%"> <el-table :data="list" style="width: 100%">
<el-table-column type="index" />
<el-table-column label="答题人" prop="answerName" /> <el-table-column label="答题人" prop="answerName" />
<el-table-column label="提交时间" prop="type" /> <el-table-column label="提交时间" prop="updateTime" />
<el-table-column label="操作" prop="options"> <el-table-column label="操作" prop="options">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleGetMain(scope.row.id)">查看详情</el-button> <el-button link type="primary" @click="handleGetMain(scope.row.id)">查看详情</el-button>
@@ -261,6 +262,7 @@ function handleGetMain(id: string) {
cursor: pointer; cursor: pointer;
margin-right: 10px; margin-right: 10px;
letter-spacing: 3px; letter-spacing: 3px;
font-size: 16px;
&:hover { &:hover {
color: #409eff; color: #409eff;
} }

View File

@@ -2,7 +2,7 @@
<div class="AddBuildingBox"> <div class="AddBuildingBox">
<PageHeader :title="userid ? '编辑问卷' : '添加问卷'"></PageHeader> <PageHeader :title="userid ? '编辑问卷' : '添加问卷'"></PageHeader>
<div class="AddBuildingBody"> <div class="AddBuildingBody">
<el-form :rules="rules" class="formBody" label-position="right" label-width="130px"> <el-form class="formBody" label-position="right" label-width="130px">
<el-card> <el-card>
<template #header> <template #header>
<span>基本信息</span> <span>基本信息</span>
@@ -25,7 +25,7 @@
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="时间范围" prop="startTime"> <el-form-item label="时间范围" required>
<el-date-picker <el-date-picker
v-model="timeData" v-model="timeData"
:disabled-date="handleDate" :disabled-date="handleDate"
@@ -82,12 +82,9 @@ const form = ref({
startTime: '', startTime: '',
endTime: '' endTime: ''
}); });
const rules = ref({
startTime: [{ required: true, message: '请选择日期范围', trigger: 'blur' }]
});
const timeData = ref([]); const timeData = ref([]);
function handleDate(data: string) { function handleDate(data: string) {
const date = new Date(); const date = new Date(new Date().toLocaleDateString());
return date.getTime() > new Date(data).getTime(); return date.getTime() > new Date(data).getTime();
} }
function handleModelDate(date: string) { function handleModelDate(date: string) {
@@ -157,12 +154,16 @@ async function handleSave(data: QuestionComponentnaireForm) {
if (timeData.value.length <= 0) { if (timeData.value.length <= 0) {
return ElMessage.warning('请选择问卷时间范围'); return ElMessage.warning('请选择问卷时间范围');
} }
const list = data.content ?? [];
if (list.length <= 0) {
return ElMessage.warning('请添加问卷题目');
}
try { try {
// 父组件自己调用接口提交 // 父组件自己调用接口提交
if (userid.value !== null) { if (userid.value !== null) {
await updateQuestionnaire({ await updateQuestionnaire({
...data, ...data,
content: JSON.stringify(data.content), content: JSON.stringify(list),
status: '0', // 草稿状态 status: '0', // 草稿状态
startTime: timeData.value[0], startTime: timeData.value[0],
endTime: timeData.value[1], endTime: timeData.value[1],
@@ -171,7 +172,7 @@ async function handleSave(data: QuestionComponentnaireForm) {
} else { } else {
await addQuestionnaire({ await addQuestionnaire({
...data, ...data,
content: JSON.stringify(data.content), content: JSON.stringify(list),
status: '0', // 草稿状态 status: '0', // 草稿状态
startTime: timeData.value[0], startTime: timeData.value[0],
endTime: timeData.value[1], endTime: timeData.value[1],
@@ -191,11 +192,15 @@ async function handlePublish(data: QuestionComponentnaireForm) {
if (timeData.value.length <= 0) { if (timeData.value.length <= 0) {
return ElMessage.warning('请选择问卷时间范围'); return ElMessage.warning('请选择问卷时间范围');
} }
const list = data.content ?? [];
if (list.length <= 0) {
return ElMessage.warning('请添加问卷题目');
}
try { try {
if (userid.value !== null) { if (userid.value !== null) {
await updateQuestionnaire({ await updateQuestionnaire({
...data, ...data,
content: JSON.stringify(data.content), content: JSON.stringify(list),
status: '1', // 发布状态 status: '1', // 发布状态
startTime: timeData.value[0], startTime: timeData.value[0],
endTime: timeData.value[1], endTime: timeData.value[1],
@@ -204,7 +209,7 @@ async function handlePublish(data: QuestionComponentnaireForm) {
} else { } else {
await addQuestionnaire({ await addQuestionnaire({
...data, ...data,
content: JSON.stringify(data.content), content: JSON.stringify(list),
status: '1', // 发布状态 status: '1', // 发布状态
endTime: timeData.value[1], endTime: timeData.value[1],
startTime: timeData.value[0], startTime: timeData.value[0],

View File

@@ -3,18 +3,18 @@
<el-row :gutter="20"> <el-row :gutter="20">
<!-- 左侧:组件库 --> <!-- 左侧:组件库 -->
<el-col :span="4"> <el-col :span="4">
<el-card shadow="hover" class="component-panel sticky-card"> <el-card class="component-panel sticky-card" shadow="hover">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>组件库</span> <span>组件库</span>
</div> </div>
</template> </template>
<draggable <draggable
:list="componentList"
:group="{ name: 'components', pull: 'clone', put: false }"
:clone="cloneComponent" :clone="cloneComponent"
item-key="type" :group="{ name: 'components', pull: 'clone', put: false }"
:list="componentList"
class="component-list" class="component-list"
item-key="type"
> >
<template #item="{ element }"> <template #item="{ element }">
<div class="component-item"> <div class="component-item">
@@ -28,7 +28,7 @@
<!-- 中间:设计画布 --> <!-- 中间:设计画布 -->
<el-col :span="12"> <el-col :span="12">
<el-card shadow="hover" class="canvas-panel"> <el-card class="canvas-panel" shadow="hover">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>设计画布</span> <span>设计画布</span>
@@ -36,24 +36,24 @@
</template> </template>
<div class="question-title-area"> <div class="question-title-area">
<el-input maxlength="20" show-word-limit v-model.trim="form.title" placeholder="请输入问卷标题" class="title-input" /> <el-input v-model.trim="form.title" class="title-input" maxlength="20" placeholder="请输入问卷标题" show-word-limit />
<el-input maxlength="200" show-word-limit v-model.trim="form.description" type="textarea" placeholder="请输入问卷描述(选填)" /> <el-input v-model.trim="form.description" maxlength="200" placeholder="请输入问卷描述(选填)" show-word-limit type="textarea" />
</div> </div>
<draggable v-model="form.content" group="components" item-key="cid" class="question-list" @add="handleAddQuestion"> <draggable v-model="form.content" class="question-list" group="components" item-key="cid" @add="handleAddQuestion">
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="question-item" :class="{ 'is-active': activeIndex === index }" @click="selectQuestion(index)"> <div :class="{ 'is-active': activeIndex === index }" class="question-item" @click="selectQuestion(index)">
<div class="question-handle"> <div class="question-handle">
<el-icon><Rank /></el-icon> <el-icon><Rank /></el-icon>
</div> </div>
<div class="question-content"> <div class="question-content">
<el-tag size="small" type="success" class="question-type-tag"> <el-tag class="question-type-tag" size="small" type="success">
{{ getComponentLabel(element.type) }} {{ getComponentLabel(element.type) }}
</el-tag> </el-tag>
<span>Q{{ index + 1 }}. {{ element.title || '未命名题目' }}</span> <span>Q{{ index + 1 }}. {{ element.title || '未命名题目' }}</span>
</div> </div>
<div class="question-actions"> <div class="question-actions">
<el-button link type="danger" :icon="Delete" @click.stop="deleteQuestion(index)"> 删除 </el-button> <el-button :icon="Delete" link type="danger" @click.stop="deleteQuestion(index)"> 删除 </el-button>
</div> </div>
</div> </div>
</template> </template>
@@ -63,16 +63,16 @@
</el-col> </el-col>
<!-- 右侧:属性配置 --> <!-- 右侧:属性配置 -->
<el-col style="position: sticky; top: 60px" :span="8"> <el-col :span="8" style="position: sticky; top: 60px">
<el-card shadow="hover" class="property-panel sticky-card" v-if="activeQuestion"> <el-card v-if="activeQuestion" class="property-panel sticky-card" shadow="hover">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>属性配置</span> <span>属性配置</span>
</div> </div>
</template> </template>
<el-form :rules="rules" :model="activeQuestion" style="position: sticky; top: 60px" label-width="80px"> <el-form :model="activeQuestion" :rules="rules" label-width="80px" style="position: sticky; top: 60px">
<el-form-item label="题目标题" prop="title"> <el-form-item label="题目标题" prop="title">
<el-input maxlength="50" v-model.trim="activeQuestion.title" placeholder="请输入题目标题" /> <el-input v-model.trim="activeQuestion.title" maxlength="50" placeholder="请输入题目标题" />
</el-form-item> </el-form-item>
<el-form-item label="是否必填"> <el-form-item label="是否必填">
<el-switch v-model.trim="activeQuestion.required" /> <el-switch v-model.trim="activeQuestion.required" />
@@ -82,18 +82,19 @@
<el-divider content-position="left">选项设置</el-divider> <el-divider content-position="left">选项设置</el-divider>
<div v-for="(opt, idx) in activeQuestion.options" :key="idx" class="option-item"> <div v-for="(opt, idx) in activeQuestion.options" :key="idx" class="option-item">
<el-input <el-input
maxlength="20"
v-model.trim="opt.label" v-model.trim="opt.label"
:class="{ 'is-duplicate': isOptionDuplicate(idx) }"
maxlength="20"
placeholder="选项内容" placeholder="选项内容"
style="width: 200px; margin-right: 10px; margin-bottom: 10px" style="width: 200px; margin-right: 10px; margin-bottom: 10px"
@blur="handleOptionBlur(idx)"
/> />
<el-button link type="danger" v-if="activeQuestion.options.length > 2" :icon="Delete" @click="activeQuestion.options.splice(idx, 1)"> <span v-if="isOptionDuplicate(idx)" class="duplicate-tip">选项重复</span>
<el-button v-if="activeQuestion.options.length > 2" :icon="Delete" link type="danger" @click="activeQuestion.options.splice(idx, 1)">
删除 删除
</el-button> </el-button>
</div> </div>
<el-button type="primary" @click="activeQuestion.options.push({ label: '选项' + (activeQuestion.options.length + 1) })"> <el-button type="primary" @click="addOption">添加选项</el-button>
添加选项
</el-button>
</template> </template>
</el-form> </el-form>
</el-card> </el-card>
@@ -110,11 +111,13 @@
</div> </div>
</template> </template>
<script setup lang="ts" name="QuestionnaireEdit"> <script lang="ts" name="QuestionnaireEdit" setup>
import draggable from 'vuedraggable'; import draggable from 'vuedraggable';
import { Delete, Rank, Tickets, Document, Edit } from '@element-plus/icons-vue'; import { Delete, Rank, Tickets, Document, Edit } from '@element-plus/icons-vue';
import { ComponentItem, QuestionComponentItem, QuestionComponentnaireForm } from '@/api/system/Questionnaire/type'; import { ComponentItem, QuestionComponentItem, QuestionComponentnaireForm } from '@/api/system/Questionnaire/type';
import { ElMessage } from 'element-plus'; // 👈 补充引入 import { ElMessage } from 'element-plus';
import { markRaw } from 'vue';
// ==================== Props & Emit ==================== // ==================== Props & Emit ====================
const props = defineProps<{ const props = defineProps<{
/** 初始问卷数据(编辑时传入) */ /** 初始问卷数据(编辑时传入) */
@@ -129,7 +132,7 @@ const emit = defineEmits<{
/** 发布问卷 */ /** 发布问卷 */
publish: [data: QuestionComponentnaireForm]; publish: [data: QuestionComponentnaireForm];
}>(); }>();
import { markRaw } from 'vue'; // 👈 引入 markRaw
// ==================== 响应式数据 ==================== // ==================== 响应式数据 ====================
const componentList = ref<ComponentItem[]>([ const componentList = ref<ComponentItem[]>([
{ type: 'radio', label: '单选题', icon: markRaw(Tickets) }, { type: 'radio', label: '单选题', icon: markRaw(Tickets) },
@@ -157,6 +160,46 @@ const activeQuestion = computed<QuestionComponentItem | null>(() => {
return form.value.content[activeIndex.value]; return form.value.content[activeIndex.value];
}); });
// ==================== 选项重复校验 ====================
/** 判断指定选项是否与其他选项内容重复(同一题内,trim 后比对) */
function isOptionDuplicate(idx: number): boolean {
const options = activeQuestion.value?.options;
if (!options) return false;
const label = options[idx]?.label?.trim();
if (!label) return false;
return options.some((opt, i) => i !== idx && opt.label?.trim() === label);
}
/** 选项失焦时弹提示 */
function handleOptionBlur(idx: number): void {
if (isOptionDuplicate(idx)) {
ElMessage.warning('选项内容不能重复');
}
}
/** 添加选项:智能生成不冲突的默认名称(避免重名) */
function addOption(): void {
const options = activeQuestion.value!.options;
let nextIndex = options.length + 1;
while (options.some((opt) => opt.label === '选项' + nextIndex)) {
nextIndex++;
}
options.push({ label: '选项' + nextIndex });
}
/** 遍历所有题目,校验选项是否重复;有重复返回错误信息,否则返回空串 */
function validateAllOptions(): string {
for (const q of form.value.content) {
if (q.type === 'radio' || q.type === 'checkbox') {
const labels = q.options.map((o) => o.label?.trim()).filter(Boolean);
if (new Set(labels).size !== labels.length) {
return `题目「${q.title || '未命名题目'}」存在重复选项`;
}
}
}
return '';
}
// ==================== 辅助方法 ==================== // ==================== 辅助方法 ====================
function getComponentLabel(type: string): string { function getComponentLabel(type: string): string {
const item = componentList.value.find((c) => c.type === type); const item = componentList.value.find((c) => c.type === type);
@@ -166,7 +209,7 @@ function getComponentLabel(type: string): string {
// 克隆组件(用于从左侧拖入中间) // 克隆组件(用于从左侧拖入中间)
function cloneComponent(component: ComponentItem): QuestionComponentItem { function cloneComponent(component: ComponentItem): QuestionComponentItem {
return { return {
cid: Date.now(), // 生成唯一ID cid: Date.now(),
type: component.type, type: component.type,
title: '', title: '',
required: true, required: true,
@@ -181,7 +224,6 @@ function selectQuestion(index: number): void {
// 添加题目后的处理 // 添加题目后的处理
function handleAddQuestion(evt: any): void { function handleAddQuestion(evt: any): void {
// 自动选中新添加的题目
activeIndex.value = evt.newIndex; activeIndex.value = evt.newIndex;
} }
@@ -198,15 +240,19 @@ function deleteQuestion(index: number): void {
// ==================== 业务方法 ==================== // ==================== 业务方法 ====================
/** 表单验证 */ /** 表单验证 */
function validateForm(): boolean { function validateForm(): boolean {
// 验证问卷标题
if (!form.value.title.trim()) { if (!form.value.title.trim()) {
ElMessage.error('请输入问卷标题'); ElMessage.error('请输入问卷标题');
return false; return false;
} }
if (form.value.content.some((item) => item.title === '')) { if (form.value.content.some((item) => !item.title?.trim())) {
ElMessage.error('请填写所有题目标题'); ElMessage.error('请填写所有题目标题');
return false; return false;
} }
const duplicateMsg = validateAllOptions();
if (duplicateMsg) {
ElMessage.error(duplicateMsg);
return false;
}
return true; return true;
} }
@@ -237,7 +283,6 @@ function init(data: Partial<QuestionComponentnaireForm>): void {
...data ...data
}; };
// 处理content字符串转对象(如果是从数据库取的)
if (typeof form.value.content === 'string') { if (typeof form.value.content === 'string') {
try { try {
form.value.content = JSON.parse(form.value.content); form.value.content = JSON.parse(form.value.content);
@@ -262,7 +307,6 @@ function reset(): void {
// ==================== 生命周期 ==================== // ==================== 生命周期 ====================
onMounted(() => { onMounted(() => {
// 如果传入了初始数据,自动初始化
if (props.initialData) { if (props.initialData) {
init(props.initialData); init(props.initialData);
} }
@@ -272,7 +316,7 @@ onMounted(() => {
defineExpose({ init, reset }); defineExpose({ init, reset });
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.edit-container { .edit-container {
position: relative; position: relative;
@@ -333,8 +377,27 @@ defineExpose({ init, reset });
} }
} }
} }
.canvas-panel { .canvas-panel {
} }
.option-item {
display: flex;
align-items: center;
.is-duplicate {
:deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #f56c6c inset;
}
}
.duplicate-tip {
color: #f56c6c;
font-size: 12px;
margin-right: 10px;
}
}
.footer-actions { .footer-actions {
width: 100%; width: 100%;
background: #fff; background: #fff;

View File

@@ -8,7 +8,7 @@
<el-row> <el-row>
<el-col v-if="type === 'private'" :span="11"> <el-col v-if="type === 'private'" :span="11">
<el-form-item label="关联房屋" prop="houseId"> <el-form-item label="关联房屋" prop="houseId">
<el-cascader v-model="form.houseId" :options="treedata" @change="handleChangeHouse" /> <el-cascader v-model="form.houseId" :options="treeData" @change="handleChangeHouse" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col v-if="type === 'private'" :span="2"></el-col> <el-col v-if="type === 'private'" :span="2"></el-col>
@@ -126,7 +126,7 @@ import { Plus } from '@element-plus/icons-vue';
import { RepairProjectVO } from '@/api/system/RepairProject/type'; import { RepairProjectVO } from '@/api/system/RepairProject/type';
const houseStore = useHouseStore(); const houseStore = useHouseStore();
const { treedata } = storeToRefs(houseStore); const treeData = ref([]);
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/repair/uploadImages'; const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/repair/uploadImages';
const router = useRouter(); const router = useRouter();
@@ -176,6 +176,9 @@ function init() {
const obj = { const obj = {
projectType: type.value === 'private' ? '1' : '0' projectType: type.value === 'private' ? '1' : '0'
}; };
houseStore.getCurrentVilageTreeHouse().then((res) => {
treeData.value = res;
});
gettreelistRepairProject(obj).then((res) => { gettreelistRepairProject(obj).then((res) => {
maintenanceItemTreeData.value = handleTreeDisable(res.data); maintenanceItemTreeData.value = handleTreeDisable(res.data);
if (route.query.id) { if (route.query.id) {
@@ -256,9 +259,11 @@ function handleChange(pop) {
holderInfo.value = pop; holderInfo.value = pop;
form.value.residentId = ''; form.value.residentId = '';
form.value.residentName = ''; form.value.residentName = '';
form.value.phone = null;
if (pop !== null) { if (pop !== null) {
form.value.residentId = holderInfo.value.id; form.value.residentId = holderInfo.value.id;
form.value.residentName = holderInfo.value.name; form.value.residentName = holderInfo.value.name;
form.value.phone = holderInfo.value.phone;
} }
} }

View File

@@ -4,20 +4,20 @@
<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" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" @submit.prevent="handleQuery">
<el-form-item label="报修状态" prop="problemStatus"> <el-form-item label="报修状态" prop="problemStatus">
<el-select v-model="queryParams.problemStatus" placeholder="请选择" clearable> <el-select v-model="queryParams.problemStatus" clearable placeholder="请选择">
<el-option v-for="dict in com_repair_status" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in com_repair_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="预约日期" prop="maintenanceItemId"> <el-form-item label="预约日期" prop="maintenanceItemId">
<el-date-picker <el-date-picker
clearable
v-model="queryParams.orderDate" v-model="queryParams.orderDate"
clearable
format="YYYY-MM-DD"
placeholder="请选择" placeholder="请选择"
type="date" type="date"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -36,58 +36,58 @@
:disable-delete="multiple" :disable-delete="multiple"
:disable-edit="single" :disable-edit="single"
:show-add="checkPermi(['repair:repair:add'])" :show-add="checkPermi(['repair:repair:add'])"
:show-edit="checkPermi(['repair:repair:edit'])"
:show-delete="checkPermi(['repair:repair:remove'])" :show-delete="checkPermi(['repair:repair:remove'])"
:show-edit="checkPermi(['repair:repair:edit'])"
add-title="个人报修" add-title="个人报修"
@update:add="handleAdd" @update:add="handleAdd"
@update:edit="handleUpdate()" @update:edit="handleUpdate()"
@update:delete="handleDelete()" @update:delete="handleDelete()"
> >
<template #actions> <template #actions>
<el-button type="default" icon="Plus" v-hasPermi="['repair:repair:add']" @click="handleAdd('public')">公共报修</el-button> <el-button v-hasPermi="['repair:repair:add']" icon="Plus" type="default" @click="handleAdd('public')">公共报修</el-button>
</template> </template>
</right-toolbar> </right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="repairList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="repairList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="报修房屋" align="center" show-overflow-tooltip prop="houseName" /> <el-table-column align="center" label="报修房屋" prop="houseName" show-overflow-tooltip />
<el-table-column label="维修项目" align="center" prop="maintenanceItemName" /> <el-table-column align="center" label="维修项目" prop="maintenanceItemName" />
<el-table-column label="问题描述" align="center" prop="problem" /> <el-table-column align="center" label="问题描述" prop="problem" />
<el-table-column label="报修人" width="100" align="center" prop="residentName" /> <el-table-column align="center" label="报修人" prop="residentName" width="100" />
<el-table-column label="联系电话" width="120" align="center" prop="phone" /> <el-table-column align="center" label="联系电话" prop="phone" width="120" />
<el-table-column label="维修人员" width="100" align="center" prop="repairName"> <el-table-column align="center" label="维修人员" prop="repairName" width="100">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.repairName ?? '---' }}</span> <span>{{ scope.row.repairName ?? '---' }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="预约日期" align="center" prop="orderDate" width="150"> <el-table-column align="center" label="预约日期" prop="orderDate" width="150">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.orderDate, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.orderDate, '{y}-{m}-{d}') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="维修状态" align="center" width="100" prop="problemStatus"> <el-table-column align="center" label="维修状态" prop="problemStatus" width="100">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_repair_status" :value="scope.row.problemStatus || ''"></dict-tag> <dict-tag :options="com_repair_status" :value="scope.row.problemStatus || ''"></dict-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleShare(scope.row)" v-has-permi="['repair:repair:query']">{{ <el-button v-has-permi="['repair:repair:query']" link type="primary" @click="handleShare(scope.row)">{{
scope.row.problemStatus === '0' ? '分配' : '详情' scope.row.problemStatus === '0' ? '分配' : '详情'
}}</el-button> }}</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['repair:repair:edit']">编辑</el-button> <el-button v-has-permi="['repair:repair:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['repair:repair:remove']">删除</el-button> <el-button v-has-permi="['repair:repair:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="Repair" lang="ts"> <script lang="ts" name="Repair" setup>
import Pagination from '@/components/Pagination/index.vue'; import Pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import RightToolbar from '@/components/RightToolbar/index.vue'; import RightToolbar from '@/components/RightToolbar/index.vue';

View File

@@ -2,7 +2,7 @@
<div class="ResidentMainBox p-2 w-full"> <div class="ResidentMainBox p-2 w-full">
<PageHeader title="报修详情"></PageHeader> <PageHeader title="报修详情"></PageHeader>
<div class="ResidentMainBody"> <div class="ResidentMainBody">
<el-row :span="24" :gutter="20"> <el-row :gutter="20" :span="24">
<el-col :span="16"> <el-col :span="16">
<!-- 车位信息 --> <!-- 车位信息 -->
<div class="mb-10"> <div class="mb-10">
@@ -69,20 +69,20 @@
<el-col :span="24"> <el-col :span="24">
<div class="carInfo_Item"> <div class="carInfo_Item">
<div class="label">附件:</div> <div class="label">附件:</div>
<div class="main fileImagebox" v-if="form.problemImageUrlList.length > 0"> <div v-if="form.problemImageUrlList.length > 0" class="main fileImagebox">
<el-image <el-image
:preview-teleported="true" v-for="(item, index) in form.problemImageUrlList"
:zoom-rate="1.2" :key="index"
:initial-index="index"
:max-scale="7" :max-scale="7"
:min-scale="0.2" :min-scale="0.2"
:preview-src-list="form.problemImageUrlList" :preview-src-list="form.problemImageUrlList"
show-progress :preview-teleported="true"
v-for="(item, index) in form.problemImageUrlList"
:initial-index="index"
:key="index"
:src="item" :src="item"
lazy :zoom-rate="1.2"
class="carInfoImage" class="carInfoImage"
lazy
show-progress
></el-image> ></el-image>
</div> </div>
</div> </div>
@@ -100,10 +100,10 @@
<div class="repair w-full h-full flex flex-items-start mt-2"> <div class="repair w-full h-full flex flex-items-start mt-2">
<div class="title">维修师傅</div> <div class="title">维修师傅</div>
<div class="preivewBodybox flex-1 ml-5"> <div class="preivewBodybox flex-1 ml-5">
<repairUser :is-multiple="false" :username="form.repairName" :userid="form.repairUserId" @change="handleChange"> </repairUser> <repairUser :is-multiple="false" :userid="form.repairUserId" :username="form.repairName" @change="handleChange"> </repairUser>
<div class="actions mt-5"> <div class="actions mt-5">
<el-button v-if="form.problemStatus === '0'" @click="handleSend" type="primary">确定派单</el-button> <el-button v-if="form.problemStatus === '0'" type="primary" @click="handleSend">确定派单</el-button>
<el-button v-if="form.problemStatus === '1'" @click="handleChangeRepairUser" type="primary">变更人员</el-button> <el-button v-if="form.problemStatus === '1'" type="primary" @click="handleChangeRepairUser">变更人员</el-button>
<el-button @click="backRouter">返回</el-button> <el-button @click="backRouter">返回</el-button>
</div> </div>
</div> </div>
@@ -133,7 +133,7 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import StepTimeline, { StepItem } from '@/components/StepTimeline/index.vue'; import StepTimeline, { StepItem } from '@/components/StepTimeline/index.vue';
import repairUser from '@/components/Holder/repairUser.vue'; import repairUser from '@/components/Holder/repairUser.vue';
@@ -222,7 +222,7 @@ async function init() {
}); });
} }
// 处理审核╬╬ // 处理审核
function handleReview() { function handleReview() {
if (reviewInfo.value.submitStatus === '1') { if (reviewInfo.value.submitStatus === '1') {
// 提交完成 // 提交完成
@@ -315,7 +315,7 @@ function handleEdit() {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.ResidentMainBox { .ResidentMainBox {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -4,49 +4,47 @@
<div class="AddBuildingBody"> <div class="AddBuildingBody">
<div class="title">基本信息</div> <div class="title">基本信息</div>
<div class="addBuildingFormBody"> <div class="addBuildingFormBody">
<el-form class="formBody" label-position="right" ref="formRef" :model="form" :rules="rules" label-width="130px"> <el-form ref="formRef" :model="form" :rules="rules" class="formBody" label-position="right" label-width="130px">
<el-form-item label="项目名称" prop="itemName"> <el-form-item label="项目名称" prop="itemName">
<el-input maxlength="20" v-model.trim="form.itemName" placeholder="请输入项目名称" /> <el-input v-model.trim="form.itemName" maxlength="20" placeholder="请输入项目名称" />
</el-form-item> </el-form-item>
<el-form-item label="收费类型" prop="typeId"> <el-form-item label="收费类型" prop="typeId">
<el-select v-model="form.typeId"> <el-select v-model="form.typeId">
<el-option <el-option
:disabled="item.status === '1'"
v-for="(item, index) in chargeItemList" v-for="(item, index) in chargeItemList"
:key="index" :key="index"
:value="item.id" :disabled="item.status === '1'"
:label="item.name" :label="item.name"
:value="item.id"
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="计费方式" prop="billingType"> <el-form-item label="计费方式" prop="billingType">
<el-radio-group v-model="form.billingType"> <el-radio-group v-model="form.billingType">
<el-radio v-for="(item, index) in com_charge_item_method" :key="index" :value="item.value" :label="item.label"></el-radio> <el-radio v-for="(item, index) in com_charge_item_method" :key="index" :label="item.label" :value="item.value"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="计算公式" prop="formula"> <el-form-item label="计算公式" prop="formula">
<el-select <el-select v-model="form.formula">
v-model="form.formula" <el-option v-for="(item, index) in com_charge_item_formula" :key="index" :label="item.label" :value="item.value"></el-option>
>
<el-option v-for="(item, index) in com_charge_item_formula" :key="index" :value="item.value" :label="item.label"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="计量单位" prop="unit" v-if="form.formula !== '2'"> <el-form-item v-if="form.formula !== '2'" label="计量单位" prop="unit">
<el-select v-model="form.unit"> <el-select v-model="form.unit">
<el-option v-for="(item, index) in com_charge_item_unit" :key="index" :value="item.value" :label="item.label"></el-option> <el-option v-for="(item, index) in com_charge_item_unit" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="费用单价"> <el-form-item label="费用单价" prop="fixedPrice">
<el-input maxlength="10" v-model.trim.number="form.fixedPrice" :placeholder="placeHolderFun(form.typeId)" /> <el-input v-model.trim.number="form.fixedPrice" :placeholder="placeHolderFun(form.typeId)" maxlength="10" />
</el-form-item> </el-form-item>
<el-form-item label="计算精度" prop="accuracy"> <el-form-item label="计算精度" prop="accuracy">
<el-select v-model="form.accuracy"> <el-select v-model="form.accuracy">
<el-option v-for="(item, index) in com_charge_item_precision" :key="index" :value="item.value" :label="item.label"></el-option> <el-option v-for="(item, index) in com_charge_item_precision" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="进位方式" prop="carryMethod"> <el-form-item label="进位方式" prop="carryMethod">
<el-select v-model="form.carryMethod"> <el-select v-model="form.carryMethod">
<el-option v-for="(item, index) in com_charge_item_carry_method" :key="index" :value="item.value" :label="item.label"></el-option> <el-option v-for="(item, index) in com_charge_item_carry_method" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- <el-form-item label="启用/停用" prop="status"> <!-- <el-form-item label="启用/停用" prop="status">
@@ -56,7 +54,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> --> </el-form-item> -->
<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.trim="form.remark" placeholder="请输入" /> <el-input v-model.trim="form.remark" :rows="5" maxlength="200" placeholder="请输入" show-word-limit type="textarea" />
</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>
@@ -68,7 +66,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { getChargeItem, updateChargeItem, addChargeItem } from '@/api/system/SdbChargeItem'; import { getChargeItem, updateChargeItem, addChargeItem } from '@/api/system/SdbChargeItem';
@@ -103,11 +101,16 @@ const rules = ref({
{ required: true, message: '请输入 项目名称', trigger: 'blur' }, { required: true, message: '请输入 项目名称', trigger: 'blur' },
{ {
validator: (_: any, value: string | number, callback: (arg0?: Error) => any) => { validator: (_: any, value: string | number, callback: (arg0?: Error) => any) => {
if (value === '' || value === undefined) { if (value === null || value === undefined || value === '') {
return callback(new Error('请输入项目名称')); return callback(new Error('请输入项目名称'));
} }
return !validator(value, 'fullFilterReg') ? callback() : callback(new Error('请输入正确的项目名称')); const strVal = String(value).trim();
if (validator(strVal, 'fullFilterReg')) {
return callback(new Error('请输入正确的项目名称'));
} }
callback();
},
trigger: ['blur', 'change']
} }
], ],
typeId: [{ required: true, message: '请选择 收费类型', trigger: 'blur' }], typeId: [{ required: true, message: '请选择 收费类型', trigger: 'blur' }],
@@ -153,7 +156,7 @@ if (route.query.id) {
} }
// !== 提交 ============================================================================= // !== 提交 =============================================================================
// 提交 // 提交
const submitForm = () => { const submitForm = async () => {
if (form.value.formula === '2') { if (form.value.formula === '2') {
form.value.unit = ''; form.value.unit = '';
} }
@@ -181,11 +184,11 @@ const cancelForm = () => {
// 车位费 物业费 // 车位费 物业费
const typeNameList = ref(['2049026520314445826']); const typeNameList = ref(['2049026520314445826']);
function placeHolderFun(typeId: string) { function placeHolderFun(typeId: string) {
return typeNameList.value.includes(typeId) ? '请输入每日费用单价' : '请输入费用单价'; return typeNameList.value.includes(typeId) ? '请输入费用单价' : '请输入费用单价';
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.AddBuildingBox { .AddBuildingBox {
padding: 15px; padding: 15px;
height: 100%; height: 100%;

View File

@@ -42,7 +42,7 @@
<el-table v-loading="loading" :data="chargeItemList" border @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="chargeItemList" border @selection-change="handleSelectionChange">
<el-table-column align="center" type="selection" width="55" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="项目名称" prop="itemName" /> <el-table-column align="center" label="项目名称" prop="itemName" />
<el-table-column align="center" label="计费方式" prop="billingType" width="100"> <el-table-column align="center" label="计费方式" prop="billingType" width="150">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_charge_item_method" :value="scope.row.billingType"></dict-tag> <dict-tag :options="com_charge_item_method" :value="scope.row.billingType"></dict-tag>
</template> </template>
@@ -84,7 +84,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip /> <el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip />
<el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作"> <el-table-column align="center" class-name="small-padding fixed-width" label="操作">
<template #default="scope"> <template #default="scope">
<el-button v-has-permi="['chargeItem:chargeItem:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button> <el-button v-has-permi="['chargeItem:chargeItem:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button v-has-permi="['chargeItem:chargeItem:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button> <el-button v-has-permi="['chargeItem:chargeItem:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>

View File

@@ -4,9 +4,23 @@
<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" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" @submit.prevent="handleQuery">
<el-form-item label="仪表名称" prop="houseId"> <el-form-item label="抄表周期" prop="recordMonth">
<el-input maxlength="20" v-model="queryParams.houseId" placeholder="请输入仪表名称" clearable @keyup.enter="handleQuery" /> <el-date-picker v-model="queryParams.recordMonth" format="YYYY-MM" placeholder="抄表月份" type="month" value-format="YYYY-MM" />
</el-form-item>
<el-form-item label="起止日期" prop="dateRange">
<el-date-picker
v-model="StartEndDate"
clearable
end-placeholder="结束"
format="YYYY-MM-DD"
range-separator="到"
start-placeholder="开始"
type="daterange"
value-format="YYYY-MM-DD"
@clear="hanleClear"
@update:model-value="handleDate"
/>
</el-form-item> </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>
@@ -22,56 +36,56 @@
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<right-toolbar <right-toolbar
:disable-delete="multiple" :disable-delete="multiple"
:show-edit="false"
:show-add="checkPermi(['payRecord:payRecord:add'])" :show-add="checkPermi(['payRecord:payRecord:add'])"
:show-delete="checkPermi(['payRecord:payRecord:remove'])" :show-delete="checkPermi(['payRecord:payRecord:remove'])"
:show-edit="false"
@update:add="handleAdd" @update:add="handleAdd"
@update:delete="handleDelete()" @update:delete="handleDelete()"
></right-toolbar> ></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="payRecordList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="payRecordList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="收费设备类型" align="center" prop="deviceType"> <el-table-column align="center" label="收费设备类型" prop="deviceType">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_waterelectricity_topup_type" :value="scope.row.deviceType"></dict-tag> <dict-tag :options="com_waterelectricity_topup_type" :value="scope.row.deviceType"></dict-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="收费项目" align="center" prop="payType"> <el-table-column align="center" label="收费项目" prop="payType">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_payrecord_pay_type" :value="scope.row.payType"></dict-tag> <dict-tag :options="com_payrecord_pay_type" :value="scope.row.payType"></dict-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="抄表周期" align="center" prop="recordMonth" width="180"> <el-table-column align="center" label="抄表周期" prop="recordMonth" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.recordMonth, '{y}-{m}') }}</span> <span>{{ parseTime(scope.row.recordMonth, '{y}-{m}') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="起止日期" width="250" align="center"> <el-table-column align="center" label="起止日期" width="250">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.startTime, '{y}/{m}/{d}') }}</span> <span>{{ parseTime(scope.row.startTime, '{y}/{m}/{d}') }}</span>
<span> - </span> <span> - </span>
<span>{{ parseTime(scope.row.endTime, '{y}/{m}/{d}') }}</span> <span>{{ parseTime(scope.row.endTime, '{y}/{m}/{d}') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间" align="center" prop="recordMonth" width="180"> <el-table-column align="center" label="创建时间" prop="recordMonth" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.recordMonth, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.recordMonth, '{y}-{m}-{d}') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleMain(scope.row)" v-has-permi="['payRecord:payRecord:query']">明细</el-button> <el-button v-has-permi="['payRecord:payRecord:query']" link type="primary" @click="handleMain(scope.row)">明细</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['payRecord:payRecord:remove']">删除</el-button> <el-button v-has-permi="['payRecord:payRecord:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改抄表周期明细对话框 --> <!-- 添加或修改抄表周期明细对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" append-to-body width="800px">
<el-form ref="payRecordListFormRef" :model="form" label-position="left"> <el-form ref="payRecordListFormRef" :model="form" label-position="left">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
@@ -148,7 +162,7 @@
</div> </div>
</template> </template>
<script setup name="PayRecord" lang="ts"> <script lang="ts" name="PayRecord" setup>
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import DictTag from '@/components/DictTag/index.vue'; import DictTag from '@/components/DictTag/index.vue';
@@ -162,7 +176,6 @@ const { com_waterelectricity_topup_type } = toRefs<any>(proxy?.useDict('com_wate
const { com_payrecord_pay_type } = toRefs<any>(proxy?.useDict('com_payrecord_pay_type')); const { com_payrecord_pay_type } = toRefs<any>(proxy?.useDict('com_payrecord_pay_type'));
const payRecordList = ref<PayRecordVO[]>([]); const payRecordList = ref<PayRecordVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const ids = ref<Array<string | number>>([]); const ids = ref<Array<string | number>>([]);
@@ -171,13 +184,20 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const payRecordFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const StartEndDate = ref([]);
function handleDate(data: string[]) {
if (data !== null) {
queryParams.value.startTime = data[0];
queryParams.value.endTime = data[1];
}
}
const initFormData: PayRecordListForm = { const initFormData: PayRecordListForm = {
id: undefined, id: undefined,
recordId: undefined, recordId: undefined,
@@ -205,13 +225,9 @@ const data = reactive<PageData<PayRecordListForm, PayRecordQuery>>({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
deviceType: undefined,
houseId: undefined,
payType: undefined,
recordMonth: undefined, recordMonth: undefined,
startTime: undefined, startTime: undefined,
endTime: undefined, endTime: undefined
params: {}
} }
}); });
@@ -232,8 +248,18 @@ const handleQuery = () => {
getList(); getList();
}; };
function hanleClear() {
queryParams.value.startTime = undefined;
queryParams.value.endTime = undefined;
StartEndDate.value = [];
}
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryParams.value.recordMonth = undefined;
queryParams.value.startTime = undefined;
queryParams.value.endTime = undefined;
StartEndDate.value = [];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
}; };
@@ -254,7 +280,7 @@ const handleAdd = () => {
}; };
/** 详情按钮操作 */ /** 详情按钮操作 */
const handleMain = async (row?: PayRecordVO) => { const handleMain = (row?: PayRecordVO) => {
router.push({ router.push({
path: '/sdb/PayRecordMain', path: '/sdb/PayRecordMain',
query: { query: {
@@ -263,13 +289,6 @@ const handleMain = async (row?: PayRecordVO) => {
}); });
}; };
/** 修改按钮操作 */
const handleUpdate = async (id: string) => {
// const res = await getPayRecordList(id);
// Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '最新抄表详细';
};
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
@@ -297,7 +316,7 @@ onMounted(() => {
}); });
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.el-table { .el-table {
height: calc(100% - 80px); height: calc(100% - 80px);
} }

View File

@@ -55,9 +55,11 @@
<span>{{ scope.row.fixedPrice }}</span> / <span>{{ scope.row.fixedPrice }}</span> /
<DictTag :options="com_charge_item_unit" :value="scope.row.unit"></DictTag> <DictTag :options="com_charge_item_unit" :value="scope.row.unit"></DictTag>
</div> </div>
<span v-else-if="scope.row.fixedPrice">{{ scope.row.fixedPrice }}{{ handleChargePeriod(scope.row.chargePeriod) }}</span>
<span v-else>---</span> <span v-else>---</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip />
<el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作" width="200"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作" width="200">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleChange(scope.row)">生成账单</el-button> <el-button link type="primary" @click="handleChange(scope.row)">生成账单</el-button>
@@ -277,7 +279,7 @@ const BillForm = ref<{
title: string; title: string;
billId: string; billId: string;
/** /**
* 收费周期 0年 1月 2日 3 季度 4永久;为空时取账单配置 * 收费周期 0年 1月 2日 3 季度 ;为空时取账单配置
*/ */
chargePeriod: string; chargePeriod: string;
chargeScope: string; chargeScope: string;
@@ -297,6 +299,19 @@ const BillForm = ref<{
periodCalcType: '0', periodCalcType: '0',
houseId: [] houseId: []
}); });
function handleChargePeriod(chargePeriod: string) {
if (chargePeriod === '0') {
return '/年';
} else if (chargePeriod === '1') {
return '/月';
} else if (chargePeriod === '2') {
return '/日';
} else if (chargePeriod === '3') {
return '/季度';
}
}
// const HolderRef = ref<InstanceType<typeof House>>(); // const HolderRef = ref<InstanceType<typeof House>>();
// // 打开模态框 // // 打开模态框
// function OpenUseTablesFun() { // function OpenUseTablesFun() {

View File

@@ -1,12 +1,12 @@
<template> <template>
<el-card style="margin-top: 20px; height: 100%"> <el-card style="margin-top: 20px; height: 100%">
<div class="p-2 flex flex-col cashierbox h-full"> <div class="p-2 flex flex-col cashierBox h-full">
<div class="tablebox"> <div class="tableBox">
<div style="margin-bottom: 20px"> <div style="margin-bottom: 20px">
<el-button @click="printPiaoju" type="primary">打印票据</el-button> <el-button type="primary" @click="printPiaoju">打印票据</el-button>
</div> </div>
<el-table :data="tabledata" @selection-change="handleSelectionChange"> <el-table :data="tableData" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="收费名称"> <el-table-column align="center" label="收费名称">
<template #default="scope"> <template #default="scope">
<div>{{ scope.row.detailsName }}</div> <div>{{ scope.row.detailsName }}</div>
@@ -42,27 +42,27 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</div> </div>
</div> </div>
<el-dialog :title="dialog_print.title" v-model="dialog_print.visible" width="1200px" append-to-body> <el-dialog v-model="dialog_print.visible" :title="dialog_print.title" append-to-body width="1200px">
<template #header> <template #header>
<div class="flex" style="justify-content: space-between; padding: 0 20px"> <div class="flex" style="justify-content: space-between; padding: 0 20px">
<div class="title">票据打印</div> <div class="title">票据打印</div>
<el-button type="primary" :loading="isLoading" @click="confimPrint">打印</el-button> <el-button :loading="isLoading" type="primary" @click="completePrint">打印</el-button>
<!-- <el-button type="primary" v-print="printOption">打印</el-button> --> <!-- <el-button type="primary" v-print="printOption">打印</el-button> -->
</div> </div>
</template> </template>
<div class="printBox" id="printBox-Bill"> <div id="printBox-Bill" class="printBox">
<div class="title">{{ print_form.title }}</div> <div class="title">{{ print_form.title }}</div>
<div class="nav gridbox"> <div class="nav gridBox">
<div>单据日期:{{ print_form.printDate }}</div> <div>单据日期:{{ print_form.printDate }}</div>
<div>房号:{{ print_form.HouseName }}</div> <div>房号:{{ print_form.HouseName }}</div>
<div>产权人:{{ print_form.residentName }}</div> <div>产权人:{{ print_form.residentName }}</div>
<div>No.</div> <div>No.</div>
</div> </div>
<div class="tablebox"> <div class="tableBox">
<table border class="printTable"> <table border class="printTable">
<thead> <thead>
<tr> <tr>
@@ -103,8 +103,8 @@
<div class="flex-1">交款人:</div> <div class="flex-1">交款人:</div>
<div class="flex-1 flex flex-items-center"> <div class="flex-1 flex flex-items-center">
<span style="white-space: nowrap">收款人: </span> <span style="white-space: nowrap">收款人: </span>
<el-input v-model="inHomelyUse" v-if="!isPrint" placeholder="请输入收款人姓名" style="width: 150px"></el-input> <el-input v-if="!isPrint" v-model="inHomelyUse" placeholder="请输入收款人姓名" style="width: 150px"></el-input>
<span style="white-space: nowrap" v-else> {{ inHomelyUse }}</span> <span v-else style="white-space: nowrap"> {{ inHomelyUse }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -113,7 +113,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import DictTag from '@/components/DictTag/index.vue'; import DictTag from '@/components/DictTag/index.vue';
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import { ref } from 'vue'; import { ref } from 'vue';
@@ -125,7 +125,7 @@ const { com_pay_status } = toRefs<any>(proxy?.useDict('com_pay_status'));
const { com_bill_charge_period } = toRefs<any>(proxy?.useDict('com_bill_charge_period')); const { com_bill_charge_period } = toRefs<any>(proxy?.useDict('com_bill_charge_period'));
const { com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_charge_item_unit')); const { com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_charge_item_unit'));
type querytype = { type queryType = {
residentId: string; residentId: string;
residentName: string; residentName: string;
houseName: string; houseName: string;
@@ -133,7 +133,7 @@ type querytype = {
}; };
const props = defineProps({ const props = defineProps({
query: { query: {
type: Object as PropType<querytype>, type: Object as PropType<queryType>,
default: () => { default: () => {
return { return {
'residentId': '', 'residentId': '',
@@ -152,14 +152,14 @@ const queryParams = ref({
pageNum: 1, pageNum: 1,
pageSize: 10 pageSize: 10
}); });
const tabledata = ref<billlistType[]>([]); const tableData = ref<billlistType[]>([]);
// 支付状态 0未缴纳 1已缴纳 2缴纳失败 // 支付状态 0未缴纳 1已缴纳 2缴纳失败
const getList = () => { const getList = () => {
billQuery({ billQuery({
residentId: props.query.residentId, residentId: props.query.residentId,
'payStatus': '1' 'payStatus': '1'
}).then((res) => { }).then((res) => {
tabledata.value = res.rows; tableData.value = res.rows;
total.value = res.total; total.value = res.total;
}); });
}; };
@@ -210,7 +210,7 @@ function printPiaoju() {
}); });
} }
const isLoading = ref(false); const isLoading = ref(false);
function confimPrint() { function completePrint() {
isLoading.value = true; isLoading.value = true;
isPrint.value = true; isPrint.value = true;
setTimeout(() => { setTimeout(() => {
@@ -222,7 +222,7 @@ function confimPrint() {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.printBox { .printBox {
color: rgba(0, 0, 0, 1); color: rgba(0, 0, 0, 1);
padding-top: 5px; padding-top: 5px;
@@ -234,7 +234,7 @@ function confimPrint() {
} }
.nav { .nav {
font-size: 12px; font-size: 12px;
&.gridbox { &.gridBox {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 10px; grid-gap: 10px;
@@ -246,7 +246,7 @@ function confimPrint() {
justify-content: space-between; justify-content: space-between;
} }
} }
.tablebox { .tableBox {
font-size: 10px; font-size: 10px;
.printTable { .printTable {
border-collapse: collapse; border-collapse: collapse;
@@ -261,7 +261,7 @@ function confimPrint() {
} }
} }
.cashierbox { .cashierBox {
font-size: 0.8rem; font-size: 0.8rem;
.el-button { .el-button {
width: 100px; width: 100px;
@@ -269,10 +269,10 @@ function confimPrint() {
border-radius: 4px; border-radius: 4px;
} }
.actionsbox { .actionsBox {
margin-top: 20px; margin-top: 20px;
} }
.tablebox { .tableBox {
margin-top: 20px; margin-top: 20px;
height: 100%; height: 100%;
.el-table { .el-table {
@@ -280,7 +280,7 @@ function confimPrint() {
} }
} }
} }
.payinfo { .payInfo {
color: rgba(0, 0, 0, 1); color: rgba(0, 0, 0, 1);
margin-top: 20px; margin-top: 20px;
padding-bottom: 10px; padding-bottom: 10px;

View File

@@ -11,17 +11,23 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="楼栋" prop="buildingId"> <el-form-item label="楼栋" prop="buildingId">
<el-select v-model="queryParams.buildingId" placeholder="请选择楼栋" @change="handleChangeBuilding"> <el-select
v-model="queryParams.buildingId"
clearable
placeholder="请选择楼栋"
@change="handleChangeBuilding"
@clear="handleClearBuildingId"
>
<el-option v-for="(item, index) in buildingList" :key="index" :label="item.buildingName" :value="item.id"></el-option> <el-option v-for="(item, index) in buildingList" :key="index" :label="item.buildingName" :value="item.id"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="单元" prop="unitNoId"> <el-form-item label="单元" prop="unitNoId">
<el-select v-model="queryParams.unitNoId" placeholder="请选择单元" @change="handleChangeUnit"> <el-select v-model="queryParams.unitNoId" clearable placeholder="请选择单元" @change="handleChangeUnit" @clear="handleClearUnit">
<el-option v-for="(item, index) in unitList" :key="index" :label="item.unitNoName" :value="item.unitNoId"></el-option> <el-option v-for="(item, index) in unitList" :key="index" :label="item.unitNoName" :value="item.unitNoId"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="房号" prop="houseId" @change="handleChangeHouseNo"> <el-form-item label="房号" prop="houseId">
<el-select v-model="queryParams.houseId" placeholder="请选择房号"> <el-select v-model="queryParams.houseId" clearable placeholder="请选择房号" @clear="handleClear('houseId')">
<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 +36,7 @@
</div> </div>
<div> <div>
<el-form-item label="收费项目" prop="itemId"> <el-form-item label="收费项目" prop="itemId">
<el-select v-model="queryParams.chargeItemId" multiple placeholder="请选择收费项目"> <el-select v-model="queryParams.chargeItemId" clearable multiple placeholder="请选择收费项目" @clear="handleClear('chargeItemId')">
<el-option <el-option
v-for="(item, index) in chargeItems" v-for="(item, index) in chargeItems"
:key="index" :key="index"
@@ -43,6 +49,7 @@
<el-form-item label="缴费周期"> <el-form-item label="缴费周期">
<el-date-picker <el-date-picker
v-model="queryParams.startDate" v-model="queryParams.startDate"
clearable
format="YYYY-MM-DD" format="YYYY-MM-DD"
placeholder="请选择开始缴费日期" placeholder="请选择开始缴费日期"
type="date" type="date"
@@ -60,31 +67,39 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="房屋分类" prop="houseUseTypeId"> <el-form-item label="房屋分类" prop="houseUseTypeId">
<el-select v-model="queryParams.houseUseTypeId"> <el-select v-model="queryParams.houseUseTypeId" clearable @clear="handleClear('houseUseTypeId')">
<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" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="缴费时间"> <el-form-item label="缴费时间">
<el-date-picker <el-date-picker
v-model="queryParams.payTimeBegin" v-model="queryParams.payTimeBegin"
clearable
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择开始缴费时间" placeholder="请选择开始缴费时间"
type="datetime" type="datetime"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
@clear="handleClearPayTime('payTimeBegin')" @clear="handleClear('payTimeBegin')"
/> />
<el-date-picker <el-date-picker
v-model="queryParams.payTimeEnd" v-model="queryParams.payTimeEnd"
clearable
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择结束缴费时间" placeholder="请选择结束缴费时间"
style="margin-left: 10px" style="margin-left: 10px"
type="datetime" type="datetime"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
@clear="handleClearPayTime('payTimeEnd')" @clear="handleClear('payTimeEnd')"
/> />
</el-form-item> </el-form-item>
<el-form-item label="业主姓名" prop="residentName"> <el-form-item label="业主姓名" prop="residentName">
<el-input v-model.trim="queryParams.residentName" maxlength="30" placeholder="请选择业主姓名"></el-input> <el-input
v-model.trim="queryParams.residentName"
clearable
maxlength="30"
placeholder="请选择业主姓名"
@clear="handleClear('residentName')"
></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<div style="text-align: right"> <div style="text-align: right">
@@ -154,13 +169,13 @@
<el-table-column align="center" label="应收金额" prop="amount" width="100" /> <el-table-column align="center" label="应收金额" prop="amount" width="100" />
<el-table-column align="center" label="缴费状态" prop="payStatus" width="100"> <el-table-column align="center" label="缴费状态" prop="payStatus" width="100">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.payStatus === '0'">未缴纳</span> <span v-if="scope.row.payStatus === PayStatus.PENDING">未缴纳</span>
<span v-else-if="scope.row.payStatus === '1'">已缴纳</span> <span v-else-if="scope.row.payStatus === PayStatus.PAID">已缴纳</span>
<span v-else-if="scope.row.payStatus === '2'">缴纳失败</span> <span v-else-if="scope.row.payStatus === PayStatus.FAILED">缴纳失败</span>
<span v-else-if="scope.row.payStatus === '3'">部分缴纳</span> <span v-else-if="scope.row.payStatus === PayStatus.PARTIAL">部分缴纳</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="应缴金额" prop="amount" width="100" /> <el-table-column align="center" label="剩余应缴金额" prop="needPayAmount" width="140" />
<el-table-column align="center" label="优惠金额" prop="singleDiscountAmount" width="100" /> <el-table-column align="center" label="优惠金额" prop="singleDiscountAmount" width="100" />
<el-table-column align="center" label="实缴金额" prop="actualAccount" width="100" /> <el-table-column align="center" label="实缴金额" prop="actualAccount" width="100" />
<el-table-column align="center" label="缴费人" prop="residentName" width="120" /> <el-table-column align="center" label="缴费人" prop="residentName" width="120" />
@@ -168,8 +183,8 @@
<el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip /> <el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip />
<el-table-column align="center" class-name="small-padding fixed-width" label="操作" width="150"> <el-table-column align="center" class-name="small-padding fixed-width" label="操作" width="150">
<template #default="scope"> <template #default="scope">
<el-button v-if="scope.row.payStatus !== '1'" link type="primary" @click="handleIncome(scope.row)">收款</el-button> <el-button v-if="scope.row.payStatus !== PayStatus.PAID" link type="primary" @click="handleIncome(scope.row)">收款</el-button>
<el-button v-if="scope.row.payStatus === '1'" link type="success" @click="printPIaoJuByOnly(scope.row)">打印票据</el-button> <el-button v-if="scope.row.payStatus === PayStatus.PAID" link type="success" @click="printPIaoJuByOnly(scope.row)">打印票据</el-button>
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
@@ -177,12 +192,10 @@
<div class="footerBox"> <div class="footerBox">
<div class="totalBox"> <div class="totalBox">
<span>合计:</span> <span>合计:</span>
<span style="padding: 0 10px">应收:{{ totalAll.totalAccount }}元</span> <span>应收:{{ totalAll.totalAccount }}元(原金额:{{ totalAll.originalAmount }}元, 优惠:{{ totalAll.totalDiscountAmount }}元)</span>
<span style="padding: 0 10px">实收:{{ totalAll.TotalPaymentAmount }}元</span> <span style="padding: 0 5px">已收:{{ totalAll.TotalPaymentAmount }}元</span>
<span style="padding: 0 10px">优惠:{{ totalAll.totalDiscountAmount }}元</span> <span style="padding: 0 5px">未收:{{ totalAll.TotalUnPaymentAmount }}元</span>
<span style="padding: 0 10px">未收:{{ totalAll.TotalUnPaymentAmount }}元</span>
</div> </div>
<pagination <pagination
v-show="total > 0" v-show="total > 0"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@@ -214,7 +227,7 @@
<div style="text-decoration: underline; padding-right: 5px; text-align: center"> <div style="text-decoration: underline; padding-right: 5px; text-align: center">
{{ item.detailsName }} &nbsp;&nbsp;(应缴:¥{{ item.needPayAmount }}) {{ item.detailsName }} &nbsp;&nbsp;(应缴:¥{{ item.needPayAmount }})
</div> </div>
<el-tooltip v-if="item.payStatus === '3'" class="box-item" effect="light" placement="top"> <el-tooltip v-if="item.payStatus === PayStatus.PENDING" class="box-item" effect="light" placement="top">
<el-icon color="#909399"><QuestionFilled /></el-icon> <el-icon color="#909399"><QuestionFilled /></el-icon>
<template #content> <template #content>
<div style="line-height: 1.8; font-size: 13px; width: 180px"> <div style="line-height: 1.8; font-size: 13px; width: 180px">
@@ -241,7 +254,7 @@
</template> </template>
</el-tooltip> </el-tooltip>
</div> </div>
<span v-if="item.payStatus === '1'">(已缴费,不计入金额)</span> <span v-if="item.payStatus === PayStatus.PAID">(已缴费,不计入金额)</span>
</div> </div>
</div> </div>
</div> </div>
@@ -288,7 +301,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="inComeFrom.payStatus !== '0'" label="优惠方式" prop="discountMethod"> <el-form-item v-if="inComeFrom.payStatus !== PayStatus.PENDING" label="优惠方式" prop="discountMethod">
<el-select v-model="inComeFrom.discountMethod" placeholder="请选择" style="width: 400px" @change="handleChangeDisCountMethod"> <el-select v-model="inComeFrom.discountMethod" placeholder="请选择" style="width: 400px" @change="handleChangeDisCountMethod">
<el-option :value="0" label="不优惠"></el-option> <el-option :value="0" label="不优惠"></el-option>
<el-option :value="2" label="金额"></el-option> <el-option :value="2" label="金额"></el-option>
@@ -325,7 +338,7 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
</div> </div>
<el-form-item v-if="inComeFrom.payStatus !== '0'" label="支付方式" prop="payType"> <el-form-item v-if="inComeFrom.payStatus !== PayStatus.PENDING" label="支付方式" prop="payType">
<div class="flex-1"> <div class="flex-1">
<div v-for="(item, index) in payList" :key="index" class="flex flex-items-center"> <div v-for="(item, index) in payList" :key="index" class="flex flex-items-center">
<div class="flex-1 flex"> <div class="flex-1 flex">
@@ -452,7 +465,7 @@ import { getCommunitylistAPI } from '@/api/system/community';
import { billDelete, billEdit, billlistType, billPrint, BillPrintType, billQuery, getAllAccount } from '@/api/system/SdbCashier'; import { billDelete, billEdit, billlistType, billPrint, BillPrintType, billQuery, getAllAccount } from '@/api/system/SdbCashier';
import { listChargeItem } from '@/api/system/SdbChargeItem'; import { listChargeItem } from '@/api/system/SdbChargeItem';
import { ref } from 'vue'; import { ref } from 'vue';
import { add, div, eq, eqMoney, gtMoney, ltMoney, minMoney, moneyAdd, mul, sub, sum, toBig } from '@/utils/money'; import { add, div, eq, eqMoney, gtMoney, minMoney, moneyAdd, mul, sub, sum, toBig } from '@/utils/money';
import { BuildingUnitlist, BuildingVo, FloorsType } from '@/api/system/house/type'; import { BuildingUnitlist, BuildingVo, FloorsType } from '@/api/system/house/type';
import { ChargeItemVO } from '@/api/system/SdbChargeItem/type'; import { ChargeItemVO } from '@/api/system/SdbChargeItem/type';
import { communitylist_rows_type } from '@/api/system/community/type'; import { communitylist_rows_type } from '@/api/system/community/type';
@@ -464,6 +477,18 @@ import { useUserStore } from '@/store/modules/user';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_pay_method, com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_pay_method', 'com_charge_item_unit')); const { com_pay_method, com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_pay_method', 'com_charge_item_unit'));
/** 支付状态 */
const PayStatus = {
/** 待缴纳 */
PENDING: '0',
/** 已缴纳 */
PAID: '1',
/** 缴纳失败 */
FAILED: '2',
/** 部分缴纳 */
PARTIAL: '3'
} as const;
const userStore = useUserStore(); const userStore = useUserStore();
const loading = ref(false); const loading = ref(false);
const ids = ref<Array<billlistType>>([]); const ids = ref<Array<billlistType>>([]);
@@ -476,15 +501,15 @@ const tabs = [
type: '全部' type: '全部'
}, },
{ {
value: 0, value: PayStatus.PENDING,
type: '待缴纳' type: '待缴纳'
}, },
{ {
value: 1, value: PayStatus.PAID,
type: '已缴纳' type: '已缴纳'
}, },
{ {
value: 3, value: PayStatus.PARTIAL,
type: '部分缴纳' type: '部分缴纳'
} }
]; ];
@@ -520,6 +545,8 @@ const queryParams = ref({
// 结束时间 // 结束时间
endDate: undefined endDate: undefined
}); });
// ts工具 排除参数
type ExcludeType = keyof Omit<typeof queryParams.value, 'villageId' | 'pageSize' | 'pageNum'>;
const villageList = ref<communitylist_rows_type[]>([]); // 小区列表 const villageList = ref<communitylist_rows_type[]>([]); // 小区列表
const buildingList = ref<BuildingVo[]>([]); // 楼栋列表 const buildingList = ref<BuildingVo[]>([]); // 楼栋列表
@@ -538,7 +565,7 @@ function getHouseTypeList() {
}); });
} }
// 切换 账单类型 // 切换 账单类型
function changeTabs(value: number) { function changeTabs(value: string) {
activeTabs.value = value; activeTabs.value = value;
queryParams.value.payStatus = value; queryParams.value.payStatus = value;
getList(); getList();
@@ -575,12 +602,17 @@ function handleChangeBuilding(val: string): void {
queryParams.value.unitNoId = undefined; queryParams.value.unitNoId = undefined;
queryParams.value.houseId = undefined; queryParams.value.houseId = undefined;
unitList.value = []; unitList.value = [];
if (val !== undefined) {
getHouselistAPI(val).then((res) => { getHouselistAPI(val).then((res) => {
unitList.value = res.data.units; unitList.value = res.data.units;
}); });
}
} }
// 选择 单元 // 选择 单元
function handleChangeUnit(val: string) { function handleChangeUnit(val: string) {
if (val === undefined) {
return;
}
const find = unitList.value.find((item) => item.unitNoId === val); const find = unitList.value.find((item) => item.unitNoId === val);
queryParams.value.houseId = undefined; queryParams.value.houseId = undefined;
houseList.value = []; houseList.value = [];
@@ -588,13 +620,7 @@ function handleChangeUnit(val: string) {
houseList.value = find.floors; houseList.value = find.floors;
} }
} }
// 选择房屋
function handleChangeHouseNo() {}
// 清空缴费时间
function handleClearPayTime(type: 'payTimeBegin' | 'payTimeEnd') {
queryParams.value[type] = undefined;
}
// 清空缴费周期 // 清空缴费周期
function handleClearChargePeriod(type: 'start' | 'end' = 'start') { function handleClearChargePeriod(type: 'start' | 'end' = 'start') {
if (type === 'start') { if (type === 'start') {
@@ -622,7 +648,8 @@ function getAllBillAmount() {
totalAll.value = { totalAll.value = {
totalDiscountAmount: res.data.discountAmount, // 总优惠金额 totalDiscountAmount: res.data.discountAmount, // 总优惠金额
TotalPaymentAmount: res.data.receivedAmount, // 总实收金额 TotalPaymentAmount: res.data.receivedAmount, // 总实收金额
totalAccount: res.data.receivableAmount, // 总金额 totalAccount: res.data.receivableAmount, // 总 应收金额
originalAmount: res.data.originalAmount, // 原始金额之和(应收+优惠)
TotalUnPaymentAmount: res.data.unpaidAmount // 未收金额 TotalUnPaymentAmount: res.data.unpaidAmount // 未收金额
}; };
}); });
@@ -634,30 +661,51 @@ const handleQuery = () => {
getList(); getList();
}; };
// 优化后
const defaultQueryParams = () => ({
pageNum: 1,
pageSize: 20,
payStatus: null as number | null,
villageId: villageId as string | undefined,
buildingId: undefined,
unitNoId: undefined,
houseId: undefined,
houseUseTypeId: undefined,
payTime: undefined,
residentName: undefined,
chargeItemId: undefined,
period: undefined,
payTimeBegin: undefined,
payTimeEnd: undefined,
startDate: undefined,
endDate: undefined
});
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value = { queryParams.value = {
startDate: undefined, ...defaultQueryParams(),
payTime: undefined, payStatus: queryParams.value.payStatus // 保留当前 tab
pageNum: 1,
pageSize: 20,
payStatus: null,
villageId: undefined,
buildingId: undefined,
unitNoId: undefined,
houseId: undefined,
chargeItemId: undefined,
period: undefined,
houseUseTypeId: undefined,
residentName: undefined,
payTimeBegin: undefined,
payTimeEnd: undefined,
endDate: undefined
}; };
queryParams.value.villageId = villageId;
handleQuery(); handleQuery();
}; };
// 清空楼栋
function handleClearBuildingId() {
queryParams.value.buildingId = undefined;
queryParams.value.unitNoId = undefined;
queryParams.value.houseId = undefined;
}
// 清空单元
function handleClearUnit() {
queryParams.value.unitNoId = undefined;
queryParams.value.houseId = undefined;
}
// 清空其他参数
function handleClear(type: ExcludeType) {
queryParams.value[type] = undefined;
}
// 控制收费 按钮 不能有已缴纳的(后端会校验) // 控制收费 按钮 不能有已缴纳的(后端会校验)
const single = ref(true); const single = ref(true);
// 控制打印票据的 按钮 不能有收费的 // 控制打印票据的 按钮 不能有收费的
@@ -666,9 +714,9 @@ const printSingle = ref(true);
const handleSelectionChange = (selection: billlistType[]) => { const handleSelectionChange = (selection: billlistType[]) => {
ids.value = selection.map((item) => item); ids.value = selection.map((item) => item);
// 不能全部都是已缴纳的 0待缴纳 1已缴纳 2缴纳失败 3混合缴纳 // 不能全部都是已缴纳的 0待缴纳 1已缴纳 2缴纳失败 3混合缴纳
single.value = ids.value.every((item) => item.payStatus === '0' || item.payStatus === '3'); single.value = ids.value.every((item) => item.payStatus === PayStatus.PENDING || item.payStatus === PayStatus.PARTIAL);
// 必须都要缴费后打印 // 必须都要缴费后打印
printSingle.value = ids.value.every((item) => item.payStatus === '1'); printSingle.value = ids.value.every((item) => item.payStatus === PayStatus.PAID);
}; };
const buttonLoading = ref(false); const buttonLoading = ref(false);
// 是否多选 // 是否多选
@@ -760,7 +808,7 @@ function handleChangeDiscountMoney(val: string) {
if (isMore) { if (isMore) {
// 过滤:只保留未支付账单,已支付直接丢弃 // 过滤:只保留未支付账单,已支付直接丢弃
const unPaidList = ids.value.filter((item) => { const unPaidList = ids.value.filter((item) => {
return item.payStatus !== '1'; return item.payStatus !== PayStatus.PAID;
}); });
// 计算优惠总额 // 计算优惠总额
@@ -819,7 +867,7 @@ function handleChangeDiscountRate(val: string) {
// 批量多选模式 // 批量多选模式
if (isMore) { if (isMore) {
const unPaidList = ids.value.filter((item) => { const unPaidList = ids.value.filter((item) => {
return item.payStatus !== '1'; return item.payStatus !== PayStatus.PAID;
}); });
// 总价格 // 总价格
const sourceTotal = sum(unPaidList.map((item) => item.needPayAmount ?? 0)); const sourceTotal = sum(unPaidList.map((item) => item.needPayAmount ?? 0));
@@ -864,7 +912,7 @@ function handleIncome(row: billlistType | false) {
ids.value.forEach((item) => { ids.value.forEach((item) => {
set.add(item.residentId); set.add(item.residentId);
}); });
if (ids.value.some((item) => item.payStatus === '1')) { if (ids.value.some((item) => item.payStatus === PayStatus.PAID)) {
return ElMessage.warning('存在已支付账单,请重新选择'); return ElMessage.warning('存在已支付账单,请重新选择');
} }
if (set.size > 1) { if (set.size > 1) {
@@ -875,7 +923,7 @@ function handleIncome(row: billlistType | false) {
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);
if (item.payStatus === '0' || item.payStatus === '3') { if (item.payStatus === PayStatus.PENDING || item.payStatus === PayStatus.PARTIAL) {
inComeFrom.value.allCost = moneyAdd(inComeFrom.value.allCost || 0, item.needPayAmount); inComeFrom.value.allCost = moneyAdd(inComeFrom.value.allCost || 0, item.needPayAmount);
} }
}); });
@@ -899,17 +947,17 @@ const submitForm = () => {
} }
buttonLoading.value = true; buttonLoading.value = true;
// 部分缴纳时 判断 支付金额 和 预计支付金额 是否相等 ,相等 则 可选择 部分缴纳 方式 // 部分缴纳时 判断 支付金额 和 预计支付金额 是否相等 ,相等 则 可选择 部分缴纳 方式
if (inComeFrom.value.payStatus === '3') { if (inComeFrom.value.payStatus === PayStatus.PARTIAL) {
if (eqMoney(totalPayAmount.value, payListAllAmount.value)) { if (eqMoney(totalPayAmount.value, payListAllAmount.value)) {
buttonLoading.value = false; buttonLoading.value = false;
return ElMessage.warning('实缴金额等于应收金额时,请选择 【已缴纳】 状态'); return ElMessage.warning('实缴金额等于应收金额时,请选择 【已缴纳】 状态');
} }
} }
// 已缴纳时 判断 支付金额 和 预计支付金额 是否相等 // 已缴纳时 判断 支付金额 和 预计支付金额 是否相等
if (inComeFrom.value.payStatus === '1') { if (inComeFrom.value.payStatus === PayStatus.PAID) {
if (!eqMoney(totalPayAmount.value, payListAllAmount.value)) { if (!eqMoney(totalPayAmount.value, payListAllAmount.value)) {
buttonLoading.value = false; buttonLoading.value = false;
return ElMessage.warning('支付金额不足,请选择 【部分缴纳】 状态'); return ElMessage.warning('支付金额不足,可以选择 【部分缴纳】 状态');
} }
} }
const obj = { const obj = {
@@ -1097,22 +1145,14 @@ function conFimPrint() {
// 删除 // 删除
async function handleDelete(row: billlistType | false) { async function handleDelete(row: billlistType | false) {
if (row) { const targetIds = row ? [row.detailsId] : ids.value.map((item) => item.detailsId);
await proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗?'); if (targetIds.length === 0) {
billDelete(row.detailsId).then(() => {
ElMessage.success('删除成功');
getList();
});
} else {
if (ids.value.length <= 0) {
return ElMessage.warning('请选择账单!'); return ElMessage.warning('请选择账单!');
} }
await proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗?'); await proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗?');
billDelete(ids.value.map((item) => item.detailsId)).then(() => { await billDelete(targetIds);
ElMessage.success('删除成功'); ElMessage.success('删除成功');
getList(); getList();
});
}
} }
onMounted(() => { onMounted(() => {
@@ -1129,8 +1169,9 @@ onMounted(() => {
// 总 表单 // 总 表单
const totalAll = ref({ const totalAll = ref({
totalDiscountAmount: '0', // 总优惠金额 totalDiscountAmount: '0', // 总优惠金额
TotalPaymentAmount: '0', // 总实收金额 TotalPaymentAmount: '0', // 总已收金额
totalAccount: '0', // 总金额 totalAccount: '0', // 总 应收金额
originalAmount: '0', // 原始金额之和(应收+优惠)
TotalUnPaymentAmount: '0' // 未收金额 TotalUnPaymentAmount: '0' // 未收金额
}); });
@@ -1138,7 +1179,7 @@ function handleChangePayTypeId(val: string, index: number) {
// 【可选兜底校验】防止异常数据出现重复 // 【可选兜底校验】防止异常数据出现重复
const repeatCount = usedPayValues.value.filter((v) => v === val).length; const repeatCount = usedPayValues.value.filter((v) => v === val).length;
if (repeatCount > 1) { if (repeatCount > 1) {
payList.value[index].payType = null; payList.value[index].payType = '';
} }
} }
const usedPayValues = computed(() => { const usedPayValues = computed(() => {
@@ -1150,15 +1191,20 @@ const isOptionDisabled = (optVal: string, currentVal: any) => {
return usedPayValues.value.includes(optVal); return usedPayValues.value.includes(optVal);
}; };
/** 支付列表项类型 */
interface PayListItem {
payType: string;
payAmount: number | null;
}
// 混合支付 列表 // 混合支付 列表
const payList = ref([ const payList = ref<PayListItem[]>([
{ {
payType: '', payType: '',
payAmount: null payAmount: null
} }
]); ]);
// 混合支付 总金额 // 混合支付 总金额
const payListAllAmount = computed(() => { const payListAllAmount = computed<number>(() => {
return sum(payList.value.map((item) => item.payAmount ?? 0)); return sum(payList.value.map((item) => item.payAmount ?? 0));
}); });
// 添加支付方式 // 添加支付方式
@@ -1172,27 +1218,32 @@ function addPayList() {
function removePayList(index: number) { function removePayList(index: number) {
payList.value.splice(index, 1); payList.value.splice(index, 1);
} }
// 数据框改变时 检测数据是否大于 总金额 // 数据框改变时 检测数据是否大于 总金额, 如果大于则自动回填剩余金额
function handleChangePay(val: string, index: number = -1) { function handleChangePay(val: string, index: number = -1) {
if (!checkMoney(val)) { if (!checkMoney(val)) {
ElMessage.warning('金额格式错误'); ElMessage.warning('金额格式错误');
payList.value[index].payAmount = null; payList.value[index].payAmount = null;
return; return;
} }
// 如果 当前行输入的金额 大于 总实缴金额
// 则在当前行,自动回填正确剩余金额
if (gtMoney(payListAllAmount.value, totalPayAmount.value)) { if (gtMoney(payListAllAmount.value, totalPayAmount.value)) {
ElMessage.warning('输入金额 总和 不能大于 总实缴金额'); ElMessage.warning('输入金额 总和 不能大于 总实缴金额');
if (index > -1) { if (index > -1) {
payList.value[index].payAmount = 0;
// 自动回填 // 自动回填
payList.value[index].payAmount = sub(totalPayAmount.value, payListAllAmount.value); // BUG: payAmount=0,让 payListAllAmount’s computed 只算其他行,然后 补 回来
} // payList.value[index].payAmount = 0;
} // payList.value[index].payAmount = sub(totalPayAmount.value, payListAllAmount.value);
// 已缴纳状态时,输入金额 总和 不能小于 总实缴金额 // 总实缴 - (全部总和 - 当前行刚输入的值) = 总实缴 - 其他行总和 = 当前行剩余应缴金额
if (inComeFrom.value.payStatus === '1') { payList.value[index].payAmount = sub(totalPayAmount.value, sub(payListAllAmount.value, val));
if (ltMoney(payListAllAmount.value, totalPayAmount.value)) {
ElMessage.warning('输入金额 总和 不能小于 总实缴金额');
} }
} }
// // 已缴纳状态时,输入金额 总和 不能小于 总实缴金额
// if (inComeFrom.value.payStatus === PayStatus.PAID) {
// if (ltMoney(payListAllAmount.value, totalPayAmount.value)) {
// ElMessage.warning('输入金额 总和 不能小于 总实缴金额');
// }
// }
} }
function handlePayListIsEmpty() { function handlePayListIsEmpty() {
@@ -1201,13 +1252,7 @@ function handlePayListIsEmpty() {
return false; return false;
} }
const invalidIndex = payList.value.findIndex( const invalidIndex = payList.value.findIndex(
(item) => (item) => item.payType === null || item.payType === undefined || item.payType === '' || item.payAmount === null || item.payAmount === undefined
item.payType === null ||
item.payType === undefined ||
item.payType === '' ||
item.payAmount === null ||
item.payAmount === undefined ||
item.payAmount === ''
); );
return invalidIndex === -1; return invalidIndex === -1;
} }

View File

@@ -5,8 +5,8 @@
<div class="title"> <div class="title">
<div>基本信息</div> <div>基本信息</div>
</div> </div>
<div class="bodybox"> <div class="bodyBox">
<el-form label-width="100px" label-position="right"> <el-form label-position="right" label-width="100px">
<el-form-item label="设备编码" prop="deviceCode"> <el-form-item label="设备编码" prop="deviceCode">
<el-input v-model="form.deviceCode" placeholder="请输入"></el-input> <el-input v-model="form.deviceCode" placeholder="请输入"></el-input>
</el-form-item> </el-form-item>
@@ -15,12 +15,12 @@
</el-form-item> </el-form-item>
<el-form-item label="分合状态"> <el-form-item label="分合状态">
<el-radio-group v-model="form.electricityRecord.status"> <el-radio-group v-model="form.electricityRecord.status">
<el-radio v-for="(item, index) in com_electricity_readrecord_status" :key="index" :value="item.value" :label="item.label"></el-radio> <el-radio v-for="(item, index) in com_electricity_readrecord_status" :key="index" :label="item.label" :value="item.value"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary">提交</el-button> <el-button type="primary">提交</el-button>
<el-button @click="routeback">返回</el-button> <el-button @click="routeBack">返回</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
@@ -28,7 +28,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { getElectricityDevice } from '@/api/system/SdbElectricityDevice'; import { getElectricityDevice } from '@/api/system/SdbElectricityDevice';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { ref } from 'vue'; import { ref } from 'vue';
@@ -49,11 +49,11 @@ const form = ref({
} }
}); });
const editid = ref(null); const editId = ref(null);
function init() { function init() {
if (route.query.id) { if (route.query.id) {
editid.value = route.query.id; editId.value = route.query.id;
getElectricityDevice(editid.value).then((res) => { getElectricityDevice(editId.value).then((res) => {
form.value = { form.value = {
...form.value, ...form.value,
...res.data ...res.data
@@ -64,17 +64,17 @@ function init() {
init(); init();
function routeback() { function routeBack() {
router.back(); router.back();
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.EditBody { .EditBody {
margin-top: 20px; margin-top: 20px;
padding: 30px; padding: 30px;
background-color: white; background-color: white;
.bodybox { .bodyBox {
width: 600px; width: 600px;
margin: 20px auto 0; margin: 20px auto 0;
} }
@@ -89,7 +89,7 @@ function routeback() {
color: #000; color: #000;
display: flex; display: flex;
align-items: center; align-items: center;
.iconbox { .iconBox {
width: 30px; width: 30px;
height: 30px; height: 30px;
margin-left: 5px; margin-left: 5px;

View File

@@ -4,12 +4,12 @@
<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="100px" @submit.prevent="handleQuery" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" label-width="100px" @submit.prevent="handleQuery">
<el-form-item label="设备编号" prop="deviceCode"> <el-form-item label="设备编号" prop="deviceCode">
<el-input maxlength="25" v-model="queryParams.deviceCode" placeholder="请输入" clearable /> <el-input v-model="queryParams.deviceCode" clearable maxlength="25" placeholder="请输入" />
</el-form-item> </el-form-item>
<el-form-item label="房屋号" prop="houseName"> <el-form-item label="房屋号" prop="houseName">
<el-input maxlength="20" v-model="queryParams.houseName" placeholder="请输入" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.houseName" clearable maxlength="20" placeholder="请输入" @keyup.enter="handleQuery" />
</el-form-item> </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>
@@ -26,7 +26,11 @@
<el-col :span="1.5"> <el-col :span="1.5">
<div style="font-size: 0.8rem">电表列表</div> <div style="font-size: 0.8rem">电表列表</div>
</el-col> </el-col>
<right-toolbar :show-add="false" :show-edit="false" :show-delete="false"> <right-toolbar :show-add="false" :show-delete="false" :show-edit="false">
<template #actions>
<el-button icon="Upload" plain type="success" @click="handleOpenDialog('UploadDevices')">导入设备</el-button>
<el-button icon="Download" plain type="warning" @click="handleExport">获取导入模板</el-button>
</template>
<template #dropdown> <template #dropdown>
<el-dropdown-item @click="handleMarkQRCode"> <el-dropdown-item @click="handleMarkQRCode">
<span>批量生成条形码</span> <span>批量生成条形码</span>
@@ -34,7 +38,7 @@
<el-dropdown-item @click="handleMarkQrcodeZip"> <el-dropdown-item @click="handleMarkQrcodeZip">
<span>批量下载条形码</span> <span>批量下载条形码</span>
</el-dropdown-item> </el-dropdown-item>
<!-- <el-dropdown-item v-has-permi="['equipment:addqrcode:electricitydevice']"> <!-- <el-dropdown-item >
<div @click="handleMarkQRCode">批量更新固件</div> <div @click="handleMarkQRCode">批量更新固件</div>
</el-dropdown-item> --> </el-dropdown-item> -->
</template> </template>
@@ -42,41 +46,42 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="ElectricityDeviceList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="ElectricityDeviceList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="设备编码" width="250" align="center" prop="deviceCode" /> <el-table-column align="center" label="设备编码" prop="deviceCode" width="250" />
<el-table-column label="设备状态" width="100" align="center" prop="status"> <el-table-column align="center" label="设备状态" prop="status" width="100">
<template #default="scope"> <template #default="scope">
<div class="readRecord" v-if="scope.row.status !== null"> <div v-if="scope.row.status !== null" class="readRecord">
<DictTag :options="com_waterdevice_on_status" :value="scope.row.status"></DictTag> <DictTag :options="com_waterdevice_on_status" :value="scope.row.status"></DictTag>
</div> </div>
<span v-else>---</span> <span v-else>---</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="分合状态" width="100" align="center"> <el-table-column align="center" label="分合状态" width="100">
<template #default="scope"> <template #default="scope">
<div class="readRecord" v-if="scope.row.electricityRecord"> <div v-if="scope.row.electricityRecord" class="readRecord">
<DictTag :options="com_electricity_readrecord_status" :value="scope.row.electricityRecord.status"></DictTag> <DictTag :options="com_electricity_readrecord_status" :value="scope.row.electricityRecord.status"></DictTag>
</div> </div>
<span v-else>---</span> <span v-else>---</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="房屋号" align="center" prop="houseName" /> <el-table-column align="center" label="房屋号" prop="houseName" />
<el-table-column label="余额" width="100" align="center"> <el-table-column align="center" label="余额" width="100">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.electricityRecord">{{ scope.row.electricityRecord.balance }}</span> <span v-if="scope.row.electricityRecord">{{ scope.row.electricityRecord.balance }}</span>
<span v-else>---</span> <span v-else>---</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="条码" width="250" align="center" prop="deleted"> <el-table-column align="center" label="条码" prop="deleted" width="250">
<template #default="scope"> <template #default="scope">
<el-image <el-image
v-if="scope.row.url && scope.row.url.includes('http')" v-if="scope.row.url && scope.row.url.includes('http')"
:src="scope.row.url"
:preview-src-list="[scope.row.url]" :preview-src-list="[scope.row.url]"
style="width: 90%; height: 100%" :src="scope.row.url"
fit="cover" fit="cover"
lazy
preview-teleported preview-teleported
style="width: 90%; height: 100%; max-width: 150px"
> >
<template #toolbar> <template #toolbar>
<el-icon @click="download(scope.row.url)"><Download /></el-icon> <el-icon @click="download(scope.row.url)"><Download /></el-icon>
@@ -85,99 +90,72 @@
<span v-else>暂未生成</span> <span v-else>暂未生成</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="抄表时间" align="center"> <el-table-column align="center" label="抄表时间">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.electricityRecord ? scope.row.electricityRecord.readTime : '--' }}</span> <span>{{ scope.row.electricityRecord ? scope.row.electricityRecord.readTime : '--' }}</span>
</template> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column width="150" label="启停" align="center" prop="deleted" v-has-permi="['equipment:isUse:electricitydevice']"> <el-table-column align="center" label="启停" prop="deleted" width="150">
<template #default="scope"> <template #default="scope">
<el-switch <el-switch
@change="(val: string) => handleChangeWater(val, scope.row)" v-if="scope.row.electricityRecord"
v-model="scope.row.deleted" :active-value="true"
active-text="启用" :before-change="
inactive-text="停止" () => {
active-value="0" // 0 合闸 1 2 拉闸
inactive-value="1" // 0 合闸 传入 1 拉闸 传入 0
const status = scope.row.electricityRecord.status === 0 ? 1 : 0;
return handleChangeWater(status, scope.row);
}
"
:inactive-value="false"
:model-value="scope.row.electricityRecord.status === 0"
active-text="合闸"
inactive-text="拉闸"
></el-switch> ></el-switch>
<span v-else>---</span>
</template> </template>
</el-table-column> --> </el-table-column>
<el-table-column min-width="120" label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作" min-width="120">
<template #default="scope"> <template #default="scope">
<div class="actions"> <div class="actions">
<el-button link type="primary" @click="handleRecode(scope.row)" >充值记录</el-button> <el-button link type="primary" @click="handleRecode(scope.row)">充值记录</el-button>
<el-button link type="primary" @click="handleQrCode(scope.row)" <el-button link type="primary" @click="handleQrCode(scope.row)">生成条码</el-button>
>生成条码</el-button <el-button link type="primary" @click="handleOpenDialog('usedWater', scope.row)">用电量</el-button>
> <el-button link type="primary" @click="handleOpenDialog('recharge', scope.row)">充值</el-button>
<el-button link type="primary" @click="handleOpenDialog('usedWater', scope.row)" <!-- <el-button link type="primary" @click="handleOpenDialog('price', scope.row)">设置价格</el-button>-->
>用电量</el-button <el-button v-if="!scope.row.houseName" link type="primary" @click="handleOpenDialog('UpdateHouse', scope.row)">绑定房屋</el-button>
> <el-button v-else link type="primary" @click="handleUnbind(scope.row)">解绑房屋</el-button>
<!-- <el-button <el-button link type="primary" @click="handleUpload(scope.row)">更新数据</el-button>
link
type="primary"
@click="handleOpenDialog('Upload', scope.row)"
v-has-permi="['equipment:updatehardware:electricitydevice']"
>更新固件</el-button
> -->
<el-button
link
v-if="!scope.row.houseName"
type="primary"
@click="handleOpenDialog('UpdateHouse', scope.row)"
>绑定房屋</el-button
>
<el-button v-else link type="primary" @click="handleUnbind(scope.row)"
>解绑房屋</el-button
>
<!-- <el-dropdown style="vertical-align: text-top">
<el-button link type="primary">
<div>
更多<el-icon><arrow-down /></el-icon>
</div>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<div @click="handleOpenDialog('usedWater', scope.row)" v-has-permi="['equipment:used:electricitydevice']">用电量</div>
</el-dropdown-item>
<el-dropdown-item>
<div @click="handleOpenDialog('Upload', scope.row)" v-has-permi="['equipment:updatehardware:electricitydevice']">更新固件</div>
</el-dropdown-item>
<el-dropdown-item>
<div @click="handleOpenDialog('UpdateHouse', scope.row)" v-has-permi="['equipment:edithouse:electricitydevice']">修改</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown> -->
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改电 设备对话框 --> <!-- 添加或修改电 设备对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" append-to-body width="500px">
<el-form v-if="dialogType === 'usedWater'" :rules="rules" ref="UsedWaterFormRef" :model="UsedWaterform" label-width="100px"> <el-form v-if="dialogType === 'usedWater'" ref="UsedWaterFormRef" :model="UsedWaterForm" :rules="rules" label-width="100px">
<el-form-item label="设备编号" prop="deviceCode"> {{ UsedWaterform.deviceCode }} </el-form-item> <el-form-item label="设备编号" prop="deviceCode"> {{ UsedWaterForm.deviceCode }} </el-form-item>
<el-form-item label="开始时间" prop="startTime"> <el-form-item label="开始时间" prop="startTime">
<el-date-picker <el-date-picker
v-model="UsedWaterForm.startTime"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="UsedWaterform.startTime"
type="datetime"
@update:model-value="handleStarTime"
placeholder="开始时间" placeholder="开始时间"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
@update:model-value="handleStarTime"
/> />
</el-form-item> </el-form-item>
<el-form-item label="结束时间" prop="endTime"> <el-form-item label="结束时间" prop="endTime">
<el-date-picker <el-date-picker
v-model="UsedWaterForm.endTime"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="UsedWaterform.endTime"
type="datetime"
@update:model-value="handleEndTime"
placeholder="结束时间" placeholder="结束时间"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
@update:model-value="handleEndTime"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -189,14 +167,14 @@
<el-form-item label="上传更新文件" prop="file"> <el-form-item label="上传更新文件" prop="file">
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
accept=".bin"
:action="uploadUrl" :action="uploadUrl"
name="file"
:headers="headerToken()"
:show-file-list="true"
class="upload-demo"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload" :before-upload="beforeAvatarUpload"
:headers="headerToken()"
:on-success="handleAvatarSuccess"
:show-file-list="true"
accept=".bin"
class="upload-demo"
name="file"
> >
<div class="uploadFileBox flex flex-items-center"> <div class="uploadFileBox flex flex-items-center">
<el-icon> <el-icon>
@@ -223,40 +201,112 @@
<el-button :loading="buttonLoading" type="primary" @click="submitForm">更新到设备</el-button> <el-button :loading="buttonLoading" type="primary" @click="submitForm">更新到设备</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-form v-else-if="dialogType === 'recharge'" ref="rechargeFormRef" :model="reChargeForm" label-width="100px">
<el-form-item label="充值金额" prop="amount">
<el-input-number v-model.trim.number="reChargeForm.amount" :max="99999" :min="0" :step="0.1"> </el-input-number
><span style="padding-left: 10px">¥</span>
</el-form-item>
<el-form-item label="支付方式" prop="payType">
<el-select v-model="reChargeForm.payType" placeholder="请选择支付方式">
<el-option v-for="(item, index) in com_pay_method" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="unSubmit">取消</el-button>
<el-button :loading="buttonLoading" type="primary" @click="submitForm">充值</el-button>
</el-form-item>
</el-form>
<el-form v-else-if="dialogType === 'price'" ref="priceFormRef" :model="priceForm" label-width="100px">
<el-form-item label="电表价格" prop="price">
<el-input-number v-model.trim.number="priceForm.price" :max="99999" :min="0" :step="0.1"> </el-input-number
><span style="padding-left: 10px">(元/kw·h)</span>
</el-form-item>
<!-- <el-form-item label="欠费阈值" prop="arrearsThreshold">-->
<!-- <el-input-number v-model.trim.number="priceForm.arrearsThreshold" :max="99999" :min="0" :step="0.1"> </el-input-number-->
<!-- ><span style="padding-left: 10px">元</span>-->
<!-- </el-form-item>-->
<el-form-item>
<el-button @click="unSubmit">取消</el-button>
<el-button :loading="buttonLoading" type="primary" @click="submitForm">设置价格</el-button>
</el-form-item>
</el-form>
<el-form
v-else-if="dialogType === 'UploadDevices'"
ref="UploadDevicesFormRef"
:model="UploadDevicesFiles"
:rule="UploadDevicesFilesRules"
label-width="120px"
>
<el-form-item label="上传设备文件" prop="file">
<el-upload
ref="UploadDevicesRef"
v-model:file-list="UploadDevicesFiles.file"
:action="uploadDevicesUrl"
:auto-upload="false"
:headers="headerToken()"
:limit="1"
:on-change="handleChangeUploadDevices"
:on-exceed="handleExceed"
:show-file-list="true"
accept=".xls, .xlsx"
class="upload-demo"
name="file"
>
<div class="uploadFileBox flex flex-items-center">
<el-icon>
<upload></upload>
</el-icon>
<div style="margin-left: 10px">上传文件</div>
</div>
<template #tip>
<div class="el-upload__tip">文件必须是 <strong>excel</strong> 文件</div>
</template>
</el-upload>
</el-form-item>
<el-form-item>
<el-button @click="unSubmit">取消</el-button>
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> 上传 </el-button>
</el-form-item>
</el-form>
<div v-show="!usedWaterInfo" class="usedWaterbox"> <div v-show="!usedWaterInfo" class="usedWaterBox">
<div class="useditem">查询用电量开始时间</div> <div class="usedItem">查询用电量开始时间</div>
<div class="useditem value">{{ UsedWaterInfo.startTime }}</div> <div class="usedItem value">{{ UsedWaterInfo.startTime }}</div>
<div class="useditem">查询用电量结束时间</div> <div class="usedItem">查询用电量结束时间</div>
<div class="useditem value">{{ UsedWaterInfo.endTime }}</div> <div class="usedItem value">{{ UsedWaterInfo.endTime }}</div>
<div class="useditem">用电量</div> <div class="usedItem">用电量</div>
<div class="useditem value">{{ UsedWaterInfo.count }}</div> <div class="usedItem value">{{ UsedWaterInfo.count }}</div>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup name="WaterDevice" lang="ts"> <script lang="ts" name="WaterDevice" setup>
import { import {
ElectricityDeviceMakerQrcode, ElectricityDeviceMakerQrcode,
ElectricityDeviceRecharge,
ElectricityDeviceUnbind, ElectricityDeviceUnbind,
getElectricityDeviceTimeRangeUsedWater, getElectricityDeviceTimeRangeUsedWater,
listElectricityDevice, listElectricityDevice,
updateElectricityDevice setElectricityDevicePrice,
setElectricityDeviceSwitchStatus,
updateElectricityDevice,
updateElectricityDeviceData,
uploadElectricityDevices
} from '@/api/system/SdbElectricityDevice'; } from '@/api/system/SdbElectricityDevice';
import { ElectricityDeviceVO } from '@/api/system/SdbElectricityDevice/type'; import { ElectricityDeviceVO } from '@/api/system/SdbElectricityDevice/type';
import { useHouseStore } from '@/store/modules/house'; import { useHouseStore } from '@/store/modules/house';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { checkPermi } from '@/utils/permission';
import { Upload } from '@element-plus/icons-vue'; import { Upload } from '@element-plus/icons-vue';
import { CascaderValue, UploadProps, UploadRawFile } from 'element-plus'; import { CascaderValue, genFileId, UploadProps, UploadRawFile } from 'element-plus';
import { exportImagesToZip } from '@/utils/exportZip'; import { exportImagesToZip } from '@/utils/exportZip';
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/electricityDevice/upload'; const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/electricityDevice/upload';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_waterdevice_on_status } = toRefs<any>(proxy?.useDict('com_waterdevice_on_status')); const { com_waterdevice_on_status, com_electricity_readrecord_status, com_pay_method } = toRefs<any>(
const { com_electricity_readrecord_status } = toRefs<any>(proxy?.useDict('com_electricity_readrecord_status')); proxy?.useDict('com_waterdevice_on_status', 'com_electricity_readrecord_status', 'com_pay_method')
);
const ElectricityDeviceList = ref<ElectricityDeviceVO[]>([]); const ElectricityDeviceList = ref<ElectricityDeviceVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
@@ -297,7 +347,7 @@ const getList = async () => {
// ! 用电量 ============================================================================= // ! 用电量 =============================================================================
const usedWaterInfo = ref(true); const usedWaterInfo = ref(true);
const UsedWaterFormRef = ref(null); const UsedWaterFormRef = ref(null);
const UsedWaterform = ref({ const UsedWaterForm = ref({
deviceCode: '', deviceCode: '',
startTime: '', startTime: '',
endTime: '' endTime: ''
@@ -314,9 +364,9 @@ const rules = {
function handleStarTime(val: string) { function handleStarTime(val: string) {
if (!val) return; if (!val) return;
const current = new Date(val).getTime(); const current = new Date(val).getTime();
if (UsedWaterform.value.endTime && UsedWaterform.value.startTime) { if (UsedWaterForm.value.endTime && UsedWaterForm.value.startTime) {
if (current > new Date(UsedWaterform.value.endTime).getTime()) { if (current > new Date(UsedWaterForm.value.endTime).getTime()) {
UsedWaterform.value.endTime = ''; UsedWaterForm.value.endTime = '';
ElMessage.warning('开始时间不能大于结束时间'); ElMessage.warning('开始时间不能大于结束时间');
} }
} }
@@ -324,9 +374,9 @@ function handleStarTime(val: string) {
function handleEndTime(val: string) { function handleEndTime(val: string) {
if (!val) return; if (!val) return;
const current = new Date(val).getTime(); const current = new Date(val).getTime();
if (UsedWaterform.value.startTime && UsedWaterform.value.endTime) { if (UsedWaterForm.value.startTime && UsedWaterForm.value.endTime) {
if (current < new Date(UsedWaterform.value.startTime).getTime()) { if (current < new Date(UsedWaterForm.value.startTime).getTime()) {
UsedWaterform.value.startTime = ''; UsedWaterForm.value.startTime = '';
ElMessage.warning('结束时间不能小于开始时间'); ElMessage.warning('结束时间不能小于开始时间');
} }
} }
@@ -457,28 +507,108 @@ function handleChange(val: CascaderValue) {
EditHouseForm.value.houseId = val[2]; EditHouseForm.value.houseId = val[2];
} }
// * house // * house
const dialogType = ref<'usedWater' | 'Upload' | 'UpdateHouse'>('usedWater');
// recharge
const reChargeForm = ref({
deviceCode: '',
amount: 0,
payType: ''
});
// recharge
const priceFormRef = ref(null);
const priceForm = ref({
deviceCode: '',
// 电价
price: 0,
// 欠费阈值
arrearsThreshold: 0
});
// devices
const UploadDevicesFormRef = ref(null);
const UploadDevicesRef = ref(null);
const UploadDevicesLoading = ref(false);
const UploadDevicesFiles = ref({
file: []
});
const UploadDevicesFilesRules = ref({
file: [{ required: true, message: '设备文件不能为空', trigger: 'blur' }],
manufacturer: [{ required: true, message: '厂商标识不能为空', trigger: 'blur' }]
});
const uploadDevicesUrl = import.meta.env.VITE_APP_BASE_API + '/electricityDevice/importData';
const beforeAvatarUploadDevices: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
// 1. 校验后缀
if (!rawFile.name.endsWith('.xls') && !rawFile.name.endsWith('.xlsx')) {
ElMessage.warning('请上传 .xls, .xlsx 后缀的文件');
return false;
}
const typeList = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/octet-stream'];
// 2. 校验文件类型(bin 文件类型)
if (!typeList.includes(rawFile.type) && rawFile.type !== '') {
ElMessage.warning('文件类型不正确,请上传标准 excel 文件');
return false;
}
// 3. 校验文件大小(不能空)
// 小于5m 1024 * 1024 * 5
if (rawFile.size <= 0) {
ElMessage.warning('上传 excel 文件不能为空');
return false;
}
if (rawFile.size >= 1024 * 1024 * 5) {
ElMessage.warning('上传 excel 文件不能超过5M');
return false;
}
return true;
};
const handleExceed: UploadProps['onExceed'] = (files) => {
UploadDevicesRef.value!.clearFiles();
const file = files[0] as UploadRawFile;
file.uid = genFileId();
UploadDevicesRef.value!.handleStart(file);
};
const handleChangeUploadDevices: UploadProps['onChange'] = (uploadFile: UploadFile) => {
if (!beforeAvatarUploadDevices(uploadFile.raw)) {
UploadDevicesRef.value!.clearFiles();
}
};
type dialogTypeList = 'usedWater' | 'Upload' | 'UpdateHouse' | 'UploadDevices' | 'recharge' | 'price';
const dialogType = ref<dialogTypeList>('usedWater');
/** 用电量 更新固件 */ /** 用电量 更新固件 */
const handleOpenDialog = (type: 'usedWater' | 'Upload' | 'UpdateHouse', row: ElectricityDeviceVO) => { const handleOpenDialog = (type: dialogTypeList, row?: ElectricityDeviceVO) => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialogType.value = type; dialogType.value = type;
dialog.title = type === 'usedWater' ? '用电量' : type === 'UpdateHouse' ? '编辑户号' : '更新固件';
if (type === 'usedWater') { if (type === 'usedWater') {
UsedWaterform.value.deviceCode = row.deviceCode; dialog.title = '用电量';
UsedWaterform.value.startTime = ''; UsedWaterForm.value.deviceCode = row.deviceCode;
UsedWaterform.value.endTime = ''; UsedWaterForm.value.startTime = '';
UsedWaterForm.value.endTime = '';
usedWaterInfo.value = true; usedWaterInfo.value = true;
} else if (type === 'Upload') { } else if (type === 'Upload') {
dialog.title = '更新固件';
UploadFileForm.value.file = ''; UploadFileForm.value.file = '';
UploadFileForm.value.deviceCode = row.deviceCode; UploadFileForm.value.deviceCode = row.deviceCode;
UploadFileForm.value.id = row.id; UploadFileForm.value.id = row.id;
} else if (type === 'recharge') {
// 充值
dialog.title = '充值';
reChargeForm.value.deviceCode = row.deviceCode;
reChargeForm.value.amount = 0;
} else if (type === 'price') {
dialog.title = '设置电价';
priceForm.value.deviceCode = row.deviceCode;
priceForm.value.price = row.price ? Number(row.price) : 0;
} else if (type === 'UpdateHouse') { } else if (type === 'UpdateHouse') {
dialog.title = '编辑户号';
EditHouseForm.value.id = row.id; EditHouseForm.value.id = row.id;
EditHouseForm.value.deviceCode = row.deviceCode; EditHouseForm.value.deviceCode = row.deviceCode;
EditHouseForm.value.houseId = ''; EditHouseForm.value.houseId = '';
getTree(); getTree();
} else if (dialogType.value === 'UploadDevices') {
dialog.title = '上传设备文件';
} }
}; };
@@ -501,20 +631,40 @@ function getTree() {
}); });
} }
// 数据上传
function handleUpload(row: ElectricityDeviceVO) {
updateElectricityDeviceData({
deviceCode: row.deviceCode
}).then(() => {
proxy?.$modal.msgSuccess('操作成功');
getList();
});
}
/** 电表 启停*/ /** 电表 启停*/
// TODO 修改电表 启停 const handleChangeWater = (val: number, row: ElectricityDeviceVO): Promise<boolean> => {
// const handleChangeWater = (val: string, row: ElectricityDeviceVO) => {}; return new Promise((resolve, reject) => {
setElectricityDeviceSwitchStatus({
deviceCode: row.deviceCode,
status: val
})
.then(() => {
proxy?.$modal.msgSuccess('操作成功');
resolve(true);
})
.catch(() => reject(false));
});
};
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
if (dialogType.value === 'usedWater') { if (dialogType.value === 'usedWater') {
if (UsedWaterform.value.startTime === '' || UsedWaterform.value.endTime === '') { if (UsedWaterForm.value.startTime === '' || UsedWaterForm.value.endTime === '') {
ElMessage.warning('请选择时间'); ElMessage.warning('请选择时间');
return; return;
} }
UsedWaterFormRef.value?.validate(async (valid: boolean) => { UsedWaterFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
await getElectricityDeviceTimeRangeUsedWater(UsedWaterform.value) await getElectricityDeviceTimeRangeUsedWater(UsedWaterForm.value)
.then((res) => { .then((res) => {
UsedWaterInfo.value.endTime = res.data.endTime; UsedWaterInfo.value.endTime = res.data.endTime;
UsedWaterInfo.value.startTime = res.data.startTime; UsedWaterInfo.value.startTime = res.data.startTime;
@@ -535,18 +685,58 @@ const submitForm = () => {
unSubmit(); unSubmit();
} }
}); });
} else if (dialogType.value === 'UploadDevices') {
UploadDevicesFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (UploadDevicesFiles.value.file.length <= 0) {
return ElMessage.warning('上传文件不能为空');
}
UploadDevicesLoading.value = true;
buttonLoading.value = true;
const formData = new FormData();
formData.append('file', UploadDevicesFiles.value.file[0].raw);
uploadElectricityDevices(formData)
.then(() => {
proxy?.$modal.msgSuccess('操作成功');
getList();
unSubmit();
})
.finally(() => {
buttonLoading.value = false;
UploadDevicesLoading.value = false;
});
}
});
} else if (dialogType.value === 'recharge') {
buttonLoading.value = true;
ElectricityDeviceRecharge(reChargeForm.value)
.then(() => {
getList();
proxy?.$modal.msgSuccess('操作成功');
unSubmit();
})
.finally(() => (buttonLoading.value = false));
} else if (dialogType.value === 'price') {
buttonLoading.value = true;
setElectricityDevicePrice(priceForm.value)
.then(() => {
getList();
proxy?.$modal.msgSuccess('操作成功');
unSubmit();
})
.finally(() => (buttonLoading.value = false));
} }
}; };
const unSubmit = () => { const unSubmit = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
dialog.title = ''; dialog.title = '';
uploadRef?.value.clearFiles(); uploadRef.value && uploadRef?.value.clearFiles();
}; };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
usedWaterInfo.value = true; usedWaterInfo.value = true;
UsedWaterform.value = { UsedWaterForm.value = {
deviceCode: '', deviceCode: '',
startTime: '', startTime: '',
endTime: '' endTime: ''
@@ -561,13 +751,20 @@ const reset = () => {
deviceCode: '', deviceCode: '',
id: '' id: ''
}; };
UploadDevicesFiles.value = {
file: []
};
UploadFileForm.value.file = ''; UploadFileForm.value.file = '';
// 上传 清空 // 上传 清空
UploadFileFormRef.value?.resetFields(); UploadFileFormRef.value?.resetFields();
// 使用电量 清空 // 绑定房屋 清空
EditHouseFormRef.value?.resetFields(); EditHouseFormRef.value?.resetFields();
// 使用电量 清空 // 使用电量 清空
UsedWaterFormRef.value?.resetFields(); UsedWaterFormRef.value?.resetFields();
UploadDevicesFormRef.value?.resetFields();
UploadDevicesRef.value && UploadDevicesRef.value?.clearFiles();
UploadDevicesLoading.value = false;
}; };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@@ -586,11 +783,11 @@ const router = useRouter();
/** 充值记录 */ /** 充值记录 */
const handleRecode = (row: ElectricityDeviceVO) => { const handleRecode = (row: ElectricityDeviceVO) => {
// 1.电表 2.电表 // 1.电表 2.水表
router.push({ router.push({
path: '/equipment/electricityTopupRecord', path: '/equipment/electricityTopupRecord',
query: { query: {
type: '2', type: '1',
houseName: row.houseName, houseName: row.houseName,
deviceCode: row.deviceCode, deviceCode: row.deviceCode,
id: row.id id: row.id
@@ -613,6 +810,12 @@ const download = (url: string) => {
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
}; };
//
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('/electricityDevice/importTemplate', {}, `电表导入设备模板_${new Date().getTime()}.xlsx`);
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -644,10 +847,10 @@ const download = (url: string) => {
height: calc(100% - 80px); height: calc(100% - 80px);
} }
.usedWaterbox { .usedWaterBox {
width: 100%; width: 100%;
border: 1px solid #eef0ff; border: 1px solid #eef0ff;
.useditem { .usedItem {
height: 42px; height: 42px;
line-height: 42px; line-height: 42px;
background-color: rgba(26, 117, 255, 0.1); background-color: rgba(26, 117, 255, 0.1);

View File

@@ -4,24 +4,24 @@
<div class="AddBuildingBody"> <div class="AddBuildingBody">
<div class="title">基本信息</div> <div class="title">基本信息</div>
<div class="addBuildingFormBody"> <div class="addBuildingFormBody">
<el-form label-position="right" class="formBody" ref="formRef" :model="form" :rules="rules" label-width="130px"> <el-form ref="formRef" :model="form" :rules="rules" class="formBody" label-position="right" label-width="130px">
<el-row> <el-row>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="区域名称" prop="areaName"> <el-form-item label="区域名称" prop="areaName">
<el-input maxlength="8" show-word-limit v-model.trim="form.areaName" placeholder="请输入区域名称" /> <el-input v-model.trim="form.areaName" maxlength="8" placeholder="请输入区域名称" show-word-limit />
</el-form-item> </el-form-item>
</el-col> </el-col>
<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="areaCode"> <el-form-item label="区域编号" prop="areaCode">
<el-input maxlength="8" v-model.trim="form.areaCode" placeholder="请输入区域编号" /> <el-input v-model.trim="form.areaCode" maxlength="8" 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="area"> <el-form-item label="区域面积" prop="area">
<el-input maxlength="8" v-model.number.trim="form.area" placeholder="请输入区域面积"> <el-input v-model.number.trim="form.area" maxlength="8" placeholder="请输入区域面积">
<template #suffix>㎡</template> <template #suffix>㎡</template>
</el-input> </el-input>
</el-form-item> </el-form-item>
@@ -29,14 +29,14 @@
<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="parkingCount"> <el-form-item label="车位数量" prop="parkingCount">
<el-input maxlength="5" v-model.number.trim="form.parkingCount" placeholder="请输入车位数量" /> <el-input v-model.number.trim="form.parkingCount" maxlength="5" placeholder="请输入车位数量" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<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.trim="form.remark" placeholder="请输入" /> <el-input v-model.trim="form.remark" :rows="5" maxlength="200" placeholder="请输入" show-word-limit type="textarea" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -50,7 +50,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
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';
@@ -114,7 +114,7 @@ const rules = ref({
{ {
validator: (_: any, value: string | number, callback: (arg0?: Error) => void) => { validator: (_: any, value: string | number, callback: (arg0?: Error) => void) => {
if (value === '' || value === null) { if (value === '' || value === null) {
return callback(new Error('请输入区域面积')); return callback();
} }
const val = Number(value); const val = Number(value);
if (Number.isNaN(val)) { if (Number.isNaN(val)) {
@@ -165,7 +165,7 @@ const cancelForm = () => {
}; };
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.AddBuildingBox { .AddBuildingBox {
padding: 15px; padding: 15px;
height: 100%; height: 100%;

View File

@@ -1,18 +1,18 @@
<template> <template>
<div class="flex flex-col h-full p-2"> <div class="flex flex-col h-full p-2">
<Pageheader title="车辆管理"></Pageheader> <pageHeader title="车辆管理"></pageHeader>
<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" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" @submit.prevent="handleQuery">
<el-form-item label="车牌号" prop="carNum"> <el-form-item label="车牌号" prop="carNum">
<el-input maxlength="12" v-model.trim="queryParams.carNum" placeholder="请输入车牌号" clearable @keyup.enter="handleQuery" /> <el-input v-model.trim="queryParams.carNum" clearable maxlength="12" placeholder="请输入车牌号" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="车辆品牌" prop="carBrand"> <el-form-item label="车辆品牌" prop="carBrand">
<el-input maxlength="12" v-model.trim="queryParams.carBrand" placeholder="请输入车辆品牌" clearable @keyup.enter="handleQuery" /> <el-input v-model.trim="queryParams.carBrand" clearable maxlength="12" placeholder="请输入车辆品牌" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="车身颜色" prop="carColor"> <el-form-item label="车身颜色" prop="carColor">
<el-input maxlength="12" v-model.trim="queryParams.carColor" placeholder="请输入车身颜色" clearable @keyup.enter="handleQuery" /> <el-input v-model.trim="queryParams.carColor" clearable maxlength="12" placeholder="请输入车身颜色" @keyup.enter="handleQuery" />
</el-form-item> </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>
@@ -30,8 +30,8 @@
:disable-delete="multiple" :disable-delete="multiple"
:disable-edit="single" :disable-edit="single"
:show-add="checkPermi(['car:car:add'])" :show-add="checkPermi(['car:car:add'])"
:show-edit="checkPermi(['car:car:edit'])"
:show-delete="checkPermi(['car:car:remove'])" :show-delete="checkPermi(['car:car:remove'])"
:show-edit="checkPermi(['car:car:edit'])"
@update:add="handleAdd" @update:add="handleAdd"
@update:edit="handleUpdate()" @update:edit="handleUpdate()"
@update:delete="handleDelete()" @update:delete="handleDelete()"
@@ -39,31 +39,31 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="carList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="carList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="车牌号" align="center" prop="carNum" /> <el-table-column align="center" label="车牌号" prop="carNum" />
<el-table-column label="车辆品牌" align="center" prop="carBrand" /> <el-table-column align="center" label="车辆品牌" prop="carBrand" />
<el-table-column label="车辆型号" align="center" prop="carModel" /> <el-table-column align="center" label="车辆型号" prop="carModel" />
<el-table-column label="车身颜色" align="center" prop="carColor" /> <el-table-column align="center" label="车身颜色" prop="carColor" />
<el-table-column label="持有人" align="center" prop="userName"> <el-table-column align="center" label="持有人" prop="userName">
<template #default="scope"> {{ scope.row.userName }} </template> <template #default="scope"> {{ scope.row.userName }} </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip /> <el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip />
<el-table-column label="操作" align="center"> <el-table-column align="center" label="操作">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['car:car:edit']">修改</el-button> <el-button v-has-permi="['car:car:edit']" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['car:car:remove']">删除</el-button> <el-button v-has-permi="['car:car:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="Car" lang="ts"> <script lang="ts" name="Car" setup>
import Pageheader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import { listCar, delCar } from '@/api/system/cars'; import { listCar, delCar } from '@/api/system/cars';
import { CarVO, CarQuery, CarForm } from '@/api/system/cars/type'; import { CarVO, CarQuery, CarForm } from '@/api/system/cars/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';

View File

@@ -39,11 +39,14 @@
<div class="label">维护人员:</div> <div class="label">维护人员:</div>
<span class="value">{{ form.maintainUserListName }}</span> <span class="value">{{ form.maintainUserListName }}</span>
</div> </div>
<div class="info_item">
<div class="label">设备保质期:</div>
<span class="value">{{ form.shelfLife }}</span>
</div>
<div class="info_item"> <div class="info_item">
<div class="label">添加时间:</div> <div class="label">添加时间:</div>
<span class="value">{{ form.createTime }}</span> <span class="value">{{ form.createTime }}</span>
</div> </div>
<div></div>
<div class="info_item"> <div class="info_item">
<div class="label">备注:</div> <div class="label">备注:</div>
<span class="value">{{ form.remark }}</span> <span class="value">{{ form.remark }}</span>
@@ -55,17 +58,17 @@
<div class="title">维修记录</div> <div class="title">维修记录</div>
<div class="addBuildingFormBody"> <div class="addBuildingFormBody">
<el-table v-loading="loading" :data="deviceRepairList"> <el-table v-loading="loading" :data="deviceRepairList">
<el-table-column type="index" align="center" /> <el-table-column align="center" type="index" />
<el-table-column label="设备名称" align="center" prop="deviceName" /> <el-table-column align="center" label="设备名称" prop="deviceName" />
<el-table-column label="设备编号" align="center" prop="deviceCode" /> <el-table-column align="center" label="设备编号" prop="deviceCode" />
<el-table-column label="维修人员" align="center" prop="maintainUserNames" /> <el-table-column align="center" label="维修人员" prop="maintainUserNames" />
<el-table-column label="故障描述" align="center" prop="faultDesc" /> <el-table-column align="center" label="故障描述" prop="faultDesc" />
<el-table-column label="维修结果" align="center" prop="repairResult"> <el-table-column align="center" label="维修结果" prop="repairResult">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_equipment_repair_status" :value="scope.row.repairResult"></dict-tag> <dict-tag :options="com_equipment_repair_status" :value="scope.row.repairResult"></dict-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleMain(scope.row)">详情</el-button> <el-button link type="primary" @click="handleMain(scope.row)">详情</el-button>
<el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button>
@@ -73,13 +76,13 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getlist" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { getDevice } from '@/api/system/equipment/archive'; import { getDevice } from '@/api/system/equipment/archive';
@@ -100,6 +103,7 @@ const form = ref({
deviceModel: '', // 型号 deviceModel: '', // 型号
area: '', // 区域 area: '', // 区域
status: '0', // 区域 status: '0', // 区域
shelfLife: '', // 设备保质期
maintainUserIds: '', // 设备维护人员 maintainUserIds: '', // 设备维护人员
maintainUserListName: '', // 设备维护人员 maintainUserListName: '', // 设备维护人员
createTime: '', // 设备维护人员 createTime: '', // 设备维护人员
@@ -109,7 +113,10 @@ const form = ref({
const userid = ref(null); const userid = ref(null);
const total = ref(0); const total = ref(0);
const loading = ref(false); const loading = ref(false);
const deviceRepairList = ref([]); type DeviceRepairVOList = DeviceRepairVO & {
maintainUserNames?: string;
};
const deviceRepairList = ref<DeviceRepairVOList[]>([]);
const queryParams = ref({ const queryParams = ref({
pageNum: 1, pageNum: 1,
@@ -124,6 +131,7 @@ function init() {
id: res.data.id, id: res.data.id,
categoryName: res.data.categoryName, categoryName: res.data.categoryName,
deviceName: res.data.deviceName, deviceName: res.data.deviceName,
shelfLife: res.data.shelfLife,
deviceCode: res.data.deviceCode, deviceCode: res.data.deviceCode,
deviceModel: res.data.deviceModel, deviceModel: res.data.deviceModel,
area: res.data.area, area: res.data.area,
@@ -139,15 +147,19 @@ function init() {
.filter((it) => it !== null) .filter((it) => it !== null)
.join(','); .join(',');
} }
getlist(); getList();
}); });
} }
} }
init(); init();
async function getlist() { function getList() {
loading.value = true; loading.value = true;
listDeviceRepair() listDeviceRepair({
deviceId: form.value.id,
pageNum: queryParams.value.pageNum,
pageSize: queryParams.value.pageSize
})
.then((rows) => { .then((rows) => {
deviceRepairList.value = rows.rows; deviceRepairList.value = rows.rows;
deviceRepairList.value.forEach((item) => { deviceRepairList.value.forEach((item) => {
@@ -177,10 +189,10 @@ const handleDelete = async (row?: DeviceRepairVO) => {
await proxy?.$modal.confirm('是否确认删除维修工单?'); await proxy?.$modal.confirm('是否确认删除维修工单?');
await delDeviceRepair(_ids); await delDeviceRepair(_ids);
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
getlist(); getList();
}; };
const handleMain = (row) => { const handleMain = (row: DeviceRepairVO) => {
router.push({ router.push({
path: '/equipment/equipmentRepairMain', path: '/equipment/equipmentRepairMain',
query: { query: {
@@ -188,7 +200,7 @@ const handleMain = (row) => {
} }
}); });
}; };
const handleEdit = (row) => { const handleEdit = (row: DeviceRepairVO) => {
router.push({ router.push({
path: '/equipment/editEquipmentRepair', path: '/equipment/editEquipmentRepair',
query: { query: {
@@ -202,7 +214,7 @@ const cancelForm = () => {
}; };
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.AddBuildingBox { .AddBuildingBox {
padding: 15px; padding: 15px;
height: 100%; height: 100%;

View File

@@ -4,9 +4,9 @@
<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="100" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" label-width="100">
<el-form-item label="设备分类名称" prop="categoryName"> <el-form-item label="设备分类名称" prop="categoryName">
<el-input maxlength="20" v-model="queryParams.categoryName" placeholder="请输入设备分类名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.categoryName" clearable maxlength="20" placeholder="请输入设备分类名称" @keyup.enter="handleQuery" />
</el-form-item> </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>
@@ -24,8 +24,8 @@
:disable-delete="multiple" :disable-delete="multiple"
:disable-edit="single" :disable-edit="single"
:show-add="checkPermi(['warehouse:deviceCategory:add'])" :show-add="checkPermi(['warehouse:deviceCategory:add'])"
:show-edit="false"
:show-delete="checkPermi(['warehouse:deviceCategory:remove'])" :show-delete="checkPermi(['warehouse:deviceCategory:remove'])"
:show-edit="false"
@update:add="handleAdd" @update:add="handleAdd"
@update:edit="handleUpdate()" @update:edit="handleUpdate()"
@update:delete="handleDelete()" @update:delete="handleDelete()"
@@ -33,39 +33,36 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="deviceCategoryList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="deviceCategoryList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="设备分类名称" align="center" prop="categoryName" /> <el-table-column align="center" label="设备分类名称" prop="categoryName" />
<el-table-column label="状态" align="center" prop="status"> <el-table-column align="center" label="状态" prop="status">
<template #default="scope"> <template #default="scope">
<el-switch :inactive-value="1" :active-value="0" v-model="scope.row.status"></el-switch> <el-switch
v-model="scope.row.status"
:active-value="0"
:before-change="() => updateDeviceSwitch(scope.row)"
:inactive-value="1"
></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="添加时间" align="center" prop="createTime" /> <el-table-column align="center" label="添加时间" prop="createTime" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['warehouse:deviceCategory:edit']">编辑</el-button> <el-button v-has-permi="['warehouse:deviceCategory:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['warehouse:deviceCategory:remove']">删除</el-button> <el-button v-has-permi="['warehouse:deviceCategory:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改设备分类对话框 --> <!-- 添加或修改设备分类对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" append-to-body width="500px">
<el-form ref="deviceCategoryFormRef" :model="form" :rules="rules" label-width="120px"> <el-form ref="deviceCategoryFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="设备分类名称" prop="categoryName"> <el-form-item label="设备分类名称" prop="categoryName">
<el-input maxlength="20" show-word-limit v-model="form.categoryName" placeholder="请输入设备分类名称" /> <el-input v-model="form.categoryName" maxlength="20" placeholder="请输入设备分类名称" show-word-limit />
</el-form-item> </el-form-item>
<el-form-item label="设备分类状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="(item, index) in com_equipment_device_category" :key="index" :value="Number(item.value)" :label="item.label"></el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item> -->
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@@ -77,13 +74,12 @@
</div> </div>
</template> </template>
<script setup name="DeviceCategory" lang="ts"> <script lang="ts" name="DeviceCategory" setup>
import { listDeviceCategory, delDeviceCategory, addDeviceCategory, updateDeviceCategory } from '@/api/system/equipment/Category'; import { listDeviceCategory, delDeviceCategory, addDeviceCategory, updateDeviceCategory } from '@/api/system/equipment/Category';
import { DeviceCategoryVO, DeviceCategoryQuery, DeviceCategoryForm } from '@/api/system/equipment/Category/type'; import { DeviceCategoryVO, DeviceCategoryQuery, DeviceCategoryForm } from '@/api/system/equipment/Category/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_equipment_device_category } = toRefs<any>(proxy?.useDict('com_equipment_device_category'));
const deviceCategoryList = ref<DeviceCategoryVO[]>([]); const deviceCategoryList = ref<DeviceCategoryVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
@@ -135,6 +131,22 @@ const getList = async () => {
}); });
}; };
function updateDeviceSwitch(row: DeviceCategoryVO): Promise<boolean> {
return new Promise((resolve, reject) => {
const obj = {
id: row.id,
status: row.status === 1 ? 0 : 1
};
updateDeviceCategory(obj)
.then(() => {
resolve(true);
})
.catch(() => {
reject(false);
});
});
}
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();

View File

@@ -4,43 +4,44 @@
<div class="AddBuildingBody"> <div class="AddBuildingBody">
<div class="title">基本信息</div> <div class="title">基本信息</div>
<div class="addBuildingFormBody"> <div class="addBuildingFormBody">
<el-form class="formBody" label-position="right" ref="formRef" :model="form" :rules="rules" label-width="130px"> <el-form ref="formRef" :model="form" :rules="rules" class="formBody" label-position="right" label-width="130px">
<el-form-item label="维修设备" prop="deviceId"> <el-form-item label="设备分类" prop="categoryId">
<el-select v-model="form.deviceId"> <el-select v-model="form.categoryId" @change="handleCategoryIdChange">
<el-option <el-option
v-for="(item, index) in deviceCategorylist" v-for="(item, index) in deviceCategoryList"
:key="index" :key="index"
:disabled="item.status === 1" :disabled="item.status === 1"
:value="item.id"
:label="item.categoryName" :label="item.categoryName"
:value="item.id"
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="设备名称" prop="deviceName"> <el-form-item label="设备名称" prop="deviceId">
<el-input maxlength="20" v-model.trim="form.deviceName" placeholder="请输入设备名称" /> <el-select v-model="form.deviceId" filterable @change="handleDeviceChange">
<el-option v-for="(item, index) in deviceList" :key="index" :label="item.deviceName" :value="item.id"></el-option>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="设备编号" prop="deviceCode"> <el-form-item label="设备编号" prop="deviceCode">
<el-input maxlength="20" v-model.trim="form.deviceCode" placeholder="请输入设备编号" /> <el-input v-model.trim="form.deviceCode" disabled maxlength="20" placeholder="请输入设备编号" />
</el-form-item> </el-form-item>
<el-form-item label="设备型号" prop="deviceModel"> <el-form-item label="设备型号" prop="deviceModel">
<el-input maxlength="20" v-model.trim="form.deviceModel" placeholder="请输入设备型号" /> <el-input v-model.trim="form.deviceModel" disabled maxlength="20" placeholder="请输入设备型号" />
</el-form-item> </el-form-item>
<el-form-item label="位置区域" prop="area"> <el-form-item label="位置区域" prop="area">
<el-input maxlength="100" show-word-limit type="textarea" :rows="2" v-model.trim="form.area" placeholder="请输入位置区域" /> <el-input v-model.trim="form.area" :rows="2" maxlength="100" placeholder="请输入位置区域" show-word-limit type="textarea" />
</el-form-item> </el-form-item>
<el-form-item label="维护人员" prop="maintainUserIds"> <el-form-item label="维护人员" prop="maintainUserIds">
<repairUser <repairUser
userType="repair"
EquipmentMaintenance
:userid="checkoutuseid"
ref="repairUserRef" ref="repairUserRef"
returnvalue="*"
:is-multiple="true" :is-multiple="true"
:userid="checkoutUseId"
returnvalue="*"
title-name="维护人员" title-name="维护人员"
userType="repair"
@change="handleCheckoutUser" @change="handleCheckoutUser"
> >
<template #default> <template #default>
<div class="checkoutuserbox flex flex-items-center justify-around" v-for="(item, index) in checkoutuser" :key="index"> <div v-for="(item, index) in checkoutUser" :key="index" class="checkoutUserBox flex flex-items-center justify-around">
<span>{{ item.nickName }}</span> <span>{{ item.nickName }}</span>
<div @click="handleDelete(item.id)"> <div @click="handleDelete(item.id)">
<el-icon> <el-icon>
@@ -48,7 +49,7 @@
</el-icon> </el-icon>
</div> </div>
</div> </div>
<div class="checkoutuserbox add flex flex-items-center justify-center" @click="() => repairUserRef.open()"> <div class="checkoutUserBox add flex flex-items-center justify-center" @click="() => repairUserRef.open()">
<el-icon> <el-icon>
<Plus></Plus> <Plus></Plus>
</el-icon> </el-icon>
@@ -58,15 +59,15 @@
</repairUser> </repairUser>
</el-form-item> </el-form-item>
<el-form-item label="故障描述" prop="faultDesc"> <el-form-item label="故障描述" prop="faultDesc">
<el-input maxlength="100" show-word-limit type="textarea" :rows="2" v-model.trim="form.faultDesc" placeholder="请输入" /> <el-input v-model.trim="form.faultDesc" :rows="2" maxlength="100" placeholder="请输入" show-word-limit type="textarea" />
</el-form-item> </el-form-item>
<el-form-item label="维修结果" prop="repairResult"> <el-form-item label="维修结果" prop="repairResult">
<el-radio-group v-model.trim="form.repairResult"> <el-radio-group v-model.trim="form.repairResult">
<el-radio v-for="(item, index) in com_equipment_repair_status" :value="item.value" :label="item.label" :key="index"></el-radio> <el-radio v-for="(item, index) in com_equipment_repair_status" :key="index" :label="item.label" :value="item.value"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input maxlength="200" show-word-limit type="textarea" :rows="5" v-model.trim="form.remark" placeholder="请输入" /> <el-input v-model.trim="form.remark" :rows="5" maxlength="200" placeholder="请输入" show-word-limit type="textarea" />
</el-form-item> </el-form-item>
<el-form-item style="margin-top: 30px"> <el-form-item style="margin-top: 30px">
@@ -78,13 +79,14 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import repairUser from '@/components/Holder/repairUser.vue'; import repairUser from '@/components/Holder/repairUser.vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { listDeviceCategory } from '@/api/system/equipment/Category'; import { listDeviceCategory } from '@/api/system/equipment/Category';
import { Delete, Plus } from '@element-plus/icons-vue'; import { Delete, Plus } from '@element-plus/icons-vue';
import { addDeviceRepair, getDeviceRepair, updateDeviceRepair } from '@/api/system/equipment/Repair'; import { addDeviceRepair, getDeviceRepair, updateDeviceRepair } from '@/api/system/equipment/Repair';
import { listDevice } from '@/api/system/equipment/archive';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_equipment_repair_status } = toRefs<any>(proxy?.useDict('com_equipment_repair_status')); const { com_equipment_repair_status } = toRefs<any>(proxy?.useDict('com_equipment_repair_status'));
@@ -96,7 +98,8 @@ const formRef = ref();
const repairUserRef = ref(); const repairUserRef = ref();
const form = ref({ const form = ref({
id: null, id: null,
deviceId: '', // 分类id、 categoryId: '', // 分类id、
deviceId: '', // 设备id
deviceName: '', // 设备名称 deviceName: '', // 设备名称
deviceCode: '', // 编号 deviceCode: '', // 编号
deviceModel: '', // 型号 deviceModel: '', // 型号
@@ -107,22 +110,40 @@ const form = ref({
remark: '' // 备注 remark: '' // 备注
}); });
const rules = ref({ const rules = ref({
deviceId: [{ required: true, message: '请选择 设备类型', trigger: 'blur' }], categoryId: [{ required: true, message: '请选择 设备分类', trigger: 'blur' }],
deviceId: [{ required: true, message: '请选择 设备', trigger: 'blur' }],
deviceName: [{ required: true, message: '请输入 设备名称', trigger: 'blur' }], deviceName: [{ required: true, message: '请输入 设备名称', trigger: 'blur' }],
faultDesc: [{ required: true, message: '请输入 故障描述', trigger: 'blur' }], faultDesc: [{ required: true, message: '请输入 故障描述', trigger: 'blur' }],
maintainUserIds: [{ required: true, message: '请选择 维修人员', trigger: 'blur' }] maintainUserIds: [{ required: true, message: '请选择 维修人员', trigger: 'blur' }]
}); });
const userid = ref(null); const userid = ref(null);
const deviceCategorylist = ref([]); // 设备列表
const deviceList = ref([]);
// 分类列表
const deviceCategoryList = ref([]);
function handleCategoryIdChange(val: string) {
listDevice({ categoryId: val, pageNum: 1, pageSize: undefined }).then((res) => {
deviceList.value = res.rows;
});
}
function handleDeviceChange(val: string) {
const find = deviceList.value.find((item) => item.id === val);
if (find) {
form.value.deviceName = find.deviceName;
form.value.deviceCode = find.deviceCode;
form.value.deviceModel = find.deviceModel;
}
}
function init() { function init() {
listDeviceCategory().then((res) => { listDeviceCategory().then((res) => {
deviceCategorylist.value = res.rows; deviceCategoryList.value = res.rows;
}); });
if (route.query.id) { if (route.query.id) {
userid.value = route.query.id; userid.value = route.query.id;
getDeviceRepair(userid.value).then((res) => { getDeviceRepair(userid.value).then((res) => {
form.value = { form.value = {
categoryId: res.data.categoryId,
id: res.data.id, id: res.data.id,
deviceName: res.data.deviceName, deviceName: res.data.deviceName,
deviceCode: res.data.deviceCode, deviceCode: res.data.deviceCode,
@@ -131,38 +152,38 @@ function init() {
maintainUserIds: res.data.maintainUserIds, maintainUserIds: res.data.maintainUserIds,
area: res.data.area, area: res.data.area,
faultDesc: res.data.faultDesc, faultDesc: res.data.faultDesc,
repairResult: res.data.repairResult, repairResult: res.data.repairResult,
remark: res.data.remark remark: res.data.remark
}; };
// 根据 分类id 回显设备
checkoutuser.value = []; handleCategoryIdChange(res.data.categoryId);
checkoutuser.value = res.data.maintainUserList ?? []; checkoutUser.value = [];
checkoutUser.value = res.data.maintainUserList ?? [];
}); });
} }
} }
init(); init();
// 用于回显维修人员
const checkoutuseid = computed(() => { const checkoutUseId = computed(() => {
return checkoutuser.value return checkoutUser.value
.map((item) => item.userId) .map((item) => item.userId)
.filter((item) => item) .filter((item) => item)
.join(','); .join(',');
}); });
const checkoutuser = ref([]); // 选择维修人员
function handleCheckoutUser(row) { const checkoutUser = ref([]);
checkoutuser.value = row; function handleCheckoutUser(row: any[]) {
checkoutUser.value = row;
} }
function handleDelete(id: string) { function handleDelete(id: string) {
checkoutuser.value = checkoutuser.value.filter((item) => item.id !== id); checkoutUser.value = checkoutUser.value.filter((item) => item.id !== id);
} }
// !== 提交 ============================================================================= // !== 提交 =============================================================================
// 提交 // 提交
const submitForm = () => { const submitForm = () => {
form.value.maintainUserIds = checkoutuser.value form.value.maintainUserIds = checkoutUser.value
.map((itme) => itme.userId) .map((item) => item.userId)
.filter((item) => item) .filter((item) => item)
.join(','); .join(',');
formRef.value?.validate(async (valid: boolean) => { formRef.value?.validate(async (valid: boolean) => {
@@ -187,7 +208,7 @@ const cancelForm = () => {
}; };
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.AddBuildingBox { .AddBuildingBox {
padding: 15px; padding: 15px;
height: 100%; height: 100%;
@@ -214,7 +235,7 @@ const cancelForm = () => {
width: 650px; width: 650px;
margin: 0 auto; margin: 0 auto;
.checkoutuserbox { .checkoutUserBox {
margin: 5px; margin: 5px;
border: 1px solid #ccc; border: 1px solid #ccc;
padding: 5px 10px; padding: 5px 10px;

View File

@@ -49,7 +49,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="备注" prop="remark" /> <el-table-column align="center" label="备注" prop="remark" />
<el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作" width="160"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作" min-width="160">
<template #default="scope"> <template #default="scope">
<el-button v-has-permi="['warehouse:deviceRepair:query']" link type="primary" @click="handleMain(scope.row)">查看详情</el-button> <el-button v-has-permi="['warehouse:deviceRepair:query']" link type="primary" @click="handleMain(scope.row)">查看详情</el-button>
<el-button v-has-permi="['warehouse:deviceRepair:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button> <el-button v-has-permi="['warehouse:deviceRepair:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>

View File

@@ -62,7 +62,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="储藏室" prop="storeroomId"> <el-form-item label="储藏室" prop="storeroomId">
<el-select v-model="form.storeroomId" class="m-2" placeholder="请选择储藏室"> <el-select v-model="form.storeroomId" class="m-2" clearable placeholder="请选择储藏室" @clear="handleClearStoreRoomId">
<el-option v-for="(item, index) in storeRoomList" :key="index" :label="item.storeroomName" :value="item.id" /> <el-option v-for="(item, index) in storeRoomList" :key="index" :label="item.storeroomName" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -132,7 +132,7 @@ const rules = ref({
trigger: 'blur' trigger: 'blur'
}, },
{ {
validator: (_, value, callback) => { validator: (_: any, value: string, callback: (arg0?: Error) => void) => {
if (value === '' || value === null) { if (value === '' || value === null) {
return callback(new Error('请输入建筑面积')); return callback(new Error('请输入建筑面积'));
} else { } else {
@@ -156,7 +156,7 @@ const rules = ref({
trigger: 'blur' trigger: 'blur'
}, },
{ {
validator: (rule, value, callback) => { validator: (_: any, value: string, callback: (arg0?: Error) => void) => {
if (value === '' || value === null) { if (value === '' || value === null) {
return callback(); return callback();
} }
@@ -178,7 +178,7 @@ const rules = ref({
trigger: 'blur' trigger: 'blur'
}, },
{ {
validator: (rule, value, callback) => { validator: (_: any, value: string, callback: (arg0?: Error) => void) => {
if (value === '' || value === null) { if (value === '' || value === null) {
return callback(); return callback();
} }
@@ -259,6 +259,11 @@ async function getEditInfo() {
}); });
} }
} }
// 清空储藏室
function handleClearStoreRoomId() {
form.value.storeroomId = '';
}
// 选择楼栋 // 选择楼栋
function handleChangeBuild(id: string) { function handleChangeBuild(id: string) {
queryChangeStore.value.buildingId = id; queryChangeStore.value.buildingId = id;
@@ -329,7 +334,7 @@ const submitForm = () => {
// 有 公摊面积 / 房屋面积 // 有 公摊面积 / 房屋面积
if (shareArea || internalArea) { if (shareArea || internalArea) {
if (shareArea + internalArea !== Number(form.value.houseArea)) { if (shareArea + internalArea !== Number(form.value.houseArea)) {
return ElMessage.warning('您填写的【套内面积】+【公摊面积】计算结果,和【建筑面积】不相等'); return ElMessage.warning('填写的【套内面积】+【公摊面积】计算结果,和【建筑面积】不相等');
} }
} }
formRef.value?.validate(async (valid: boolean) => { formRef.value?.validate(async (valid: boolean) => {

View File

@@ -7,39 +7,39 @@
<el-button v-if="HouseInfo.house" v-has-permi="['system:house:edit']" text type="primary" @click="editHouse">编辑</el-button> <el-button v-if="HouseInfo.house" v-has-permi="['system:house:edit']" text type="primary" @click="editHouse">编辑</el-button>
</div> </div>
<div v-if="HouseInfo.house" class="addBuildingFormBody"> <div v-if="HouseInfo.house" class="addBuildingFormBody">
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">房号:</div> <div class="label">房号:</div>
<div class="value">{{ HouseInfo.house?.buildingName }}-{{ HouseInfo.house?.unitNoName }}-{{ HouseInfo.house?.houseNo ?? '--' }}</div> <div class="value">{{ HouseInfo.house?.buildingName }}-{{ HouseInfo.house?.unitNoName }}-{{ HouseInfo.house?.houseNo ?? '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">所属小区:</div> <div class="label">所属小区:</div>
<div class="value">{{ HouseInfo.house?.villageName ?? '--' }}</div> <div class="value">{{ HouseInfo.house?.villageName ?? '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">楼栋:</div> <div class="label">楼栋:</div>
<div class="value">{{ HouseInfo.house?.buildingName ?? '--' }}</div> <div class="value">{{ HouseInfo.house?.buildingName ?? '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">单元:</div> <div class="label">单元:</div>
<div class="value">{{ HouseInfo.house?.unitNoName ?? '--' }}</div> <div class="value">{{ HouseInfo.house?.unitNoName ?? '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">楼层:</div> <div class="label">楼层:</div>
<div class="value">{{ HouseInfo.house?.floorNo ? HouseInfo.house?.floorNo + ' 层' : '--' }}</div> <div class="value">{{ HouseInfo.house?.floorNo ? HouseInfo.house?.floorNo + ' 层' : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">建筑面积:</div> <div class="label">建筑面积:</div>
<div class="value">{{ HouseInfo.house?.houseArea ? `${HouseInfo.house?.houseArea} ㎡` : '--' }}</div> <div class="value">{{ HouseInfo.house?.houseArea ? `${HouseInfo.house?.houseArea} ㎡` : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">套内面积:</div> <div class="label">套内面积:</div>
<div class="value">{{ HouseInfo.house?.internalArea ? `${HouseInfo.house?.internalArea} ㎡` : '--' }}</div> <div class="value">{{ HouseInfo.house?.internalArea ? `${HouseInfo.house?.internalArea} ㎡` : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">公摊面积:</div> <div class="label">公摊面积:</div>
<div class="value">{{ HouseInfo.house?.shareArea ? `${HouseInfo.house?.shareArea} ㎡` : '--' }}</div> <div class="value">{{ HouseInfo.house?.shareArea ? `${HouseInfo.house?.shareArea} ㎡` : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">户型:</div> <div class="label">户型:</div>
<div class="value"> <div class="value">
{{ HouseInfo.house?.houseType ? HouseInfo.house?.houseType.split('')[0] : '--' }}室 {{ HouseInfo.house?.houseType ? HouseInfo.house?.houseType.split('')[0] : '--' }}室
@@ -60,31 +60,31 @@
</div> </div>
<div v-if="HouseInfo.storeroom" class=""> <div v-if="HouseInfo.storeroom" class="">
<div class="addBuildingFormBody"> <div class="addBuildingFormBody">
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">楼栋:</div> <div class="label">楼栋:</div>
<div class="value">{{ HouseInfo.storeroom?.buildingName ?? '--' }}</div> <div class="value">{{ HouseInfo.storeroom?.buildingName ?? '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">单元:</div> <div class="label">单元:</div>
<div class="value">{{ HouseInfo.storeroom?.unitNoName ? `${HouseInfo.storeroom?.unitNoName} 单元` : '--' }}</div> <div class="value">{{ HouseInfo.storeroom?.unitNoName ? `${HouseInfo.storeroom?.unitNoName} 单元` : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">储藏室号:</div> <div class="label">储藏室号:</div>
<div class="value">{{ HouseInfo.storeroom?.storeroomNo ? `${HouseInfo.storeroom?.storeroomNo}` : '--' }}</div> <div class="value">{{ HouseInfo.storeroom?.storeroomNo ? `${HouseInfo.storeroom?.storeroomNo}` : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">建筑面积:</div> <div class="label">建筑面积:</div>
<div class="value">{{ HouseInfo.storeroom?.houseArea ? HouseInfo.storeroom?.houseArea + ' ㎡' : '--' }}</div> <div class="value">{{ HouseInfo.storeroom?.houseArea ? HouseInfo.storeroom?.houseArea + ' ㎡' : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">公摊面积:</div> <div class="label">公摊面积:</div>
<div class="value">{{ HouseInfo.storeroom?.shareArea ? HouseInfo.storeroom?.shareArea + ' ㎡' : '--' }}</div> <div class="value">{{ HouseInfo.storeroom?.shareArea ? HouseInfo.storeroom?.shareArea + ' ㎡' : '--' }}</div>
</div> </div>
<div class="descriptionsbox"> <div class="descriptionsBox">
<div class="label">实用面积:</div> <div class="label">实用面积:</div>
<div class="value">{{ HouseInfo.storeroom?.internalArea ? HouseInfo.storeroom?.internalArea + ' ㎡' : '--' }}㎡</div> <div class="value">{{ HouseInfo.storeroom?.internalArea ? HouseInfo.storeroom?.internalArea + ' ㎡' : '--' }}㎡</div>
</div> </div>
<div class="descriptionsbox" style="grid-column-start: 1; grid-column-end: 4"> <div class="descriptionsBox" style="grid-column-start: 1; grid-column-end: 4">
<div class="label">备注:</div> <div class="label">备注:</div>
<div class="value">{{ HouseInfo.storeroom?.remark ?? '--' }}</div> <div class="value">{{ HouseInfo.storeroom?.remark ?? '--' }}</div>
</div> </div>
@@ -141,12 +141,12 @@
<el-table-column label="收费项目" prop="itemName" /> <el-table-column label="收费项目" prop="itemName" />
<el-table-column label="收费标准" prop="name"> <el-table-column label="收费标准" prop="name">
<template #default="scope"> <template #default="scope">
<div class="flex"> <div v-if="scope.row.fixedPrice" class="flex">
<span>{{ scope.row.fixedPrice }}</span> <span>{{ scope.row.fixedPrice }}</span>
<span v-if="scope.row.fixedPrice && scope.row.unit">/</span> <span v-if="scope.row.unit">/</span>
<span v-else>---</span> <dict-tag v-if="scope.row.unit" :options="com_charge_item_unit" :value="scope.row.unit"></dict-tag>
<dict-tag :options="com_charge_item_unit" :value="scope.row.unit"></dict-tag>
</div> </div>
<span v-else> --- </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="开始时间" prop="createTime" /> <el-table-column label="开始时间" prop="createTime" />
@@ -178,7 +178,7 @@
<DictTag :options="com_resident_relationship" :value="scope.row.ownerRelationship"></DictTag> <DictTag :options="com_resident_relationship" :value="scope.row.ownerRelationship"></DictTag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" prop="name" /> <el-table-column label="备注" prop="remark" show-overflow-tooltip />
</el-table> </el-table>
</div> </div>
</div> </div>
@@ -188,23 +188,51 @@
<span>缴费账单</span> <span>缴费账单</span>
</div> </div>
<div> <div>
<el-table :data="HouseInfo.billResidentList" style="width: 100%"> <el-table :data="billList" style="width: 100%; max-height: 500px; overflow: auto">
<el-table-column label="账单编号" prop="billCode" /> <el-table-column label="账单名称" prop="detailsName" />
<el-table-column label="账单名称" prop="billName" /> <el-table-column label="账单项目" prop="chargeItemName" />
<el-table-column label="账单项目" prop="chargeTypeName" />
<el-table-column label="金额" prop="money"> <el-table-column label="金额" prop="money">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.chargeTypeName === '水费' || scope.row.chargeTypeName === '电费'">--</span> <div class="flex" style="width: 120px; justify-content: start; align-items: center">
<span v-else>{{ scope.row.money }}</span> <div style="width: 80px; text-align: left">
{{ scope.row.amount }}
</div>
<el-tooltip v-if="scope.row.payStatus === '3'" class="box-item" effect="light" placement="top">
<el-icon color="#909399"><QuestionFilled /></el-icon>
<template #content>
<div style="line-height: 1.8; font-size: 13px; width: 180px">
<div class="flex justify-between" style="text-align: right">
<span>总金额</span>
<span>{{ scope.row.amount }}¥</span>
</div>
<div class="flex justify-between" style="text-align: right">
<span>优惠金额</span>
<span>− {{ scope.row.singleDiscountAmount }}¥</span>
</div>
<div class="flex justify-between" style="text-align: right">
<span>已交金额</span>
<span>− {{ scope.row.paidAmount }}¥</span>
</div>
<div
class="flex justify-between cursor-pointer"
style="margin-top: 6px; border-top: 1px solid #ccc; text-align: right; padding-top: 6px"
>
<span>待缴金额</span>
<span style="font-weight: bold; color: red">{{ scope.row.needPayAmount }}¥</span>
</div>
</div>
</template>
</el-tooltip>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="支付状态" prop="payStatus"> <el-table-column label="支付状态" prop="payStatus">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.chargeTypeName === '水费' || scope.row.chargeTypeName === '电费'">--</span>
<DictTag :options="com_pay_status" :value="scope.row.payStatus ?? 0"></DictTag> <DictTag :options="com_pay_status" :value="scope.row.payStatus ?? 0"></DictTag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</div> </div>
</div> </div>
</div> </div>
@@ -214,7 +242,9 @@
import { ref } from 'vue'; import { ref } from 'vue';
import DictTag from '@/components/DictTag/index.vue'; import DictTag from '@/components/DictTag/index.vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { Data, getHouseMain } from '@/api/system/house'; import { Data, getHouseMain, House } from '@/api/system/house';
import { billlistType, billQuery } from '@/api/system/SdbCashier';
import { QuestionFilled } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_resident_relationship } = toRefs<any>(proxy?.useDict('com_resident_relationship')); const { com_resident_relationship } = toRefs<any>(proxy?.useDict('com_resident_relationship'));
@@ -226,7 +256,7 @@ const { com_charge_item_unit } = toRefs<any>(proxy?.useDict('com_charge_item_uni
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const routeid = ref(''); let routeId = '';
const HouseInfo = ref<Data>({ const HouseInfo = ref<Data>({
billResidentList: [], billResidentList: [],
chargeItemList: [], chargeItemList: [],
@@ -263,13 +293,40 @@ function handleEditUser(id: string) {
} }
function init() { function init() {
if (route.query.id) { if (route.query.id) {
routeid.value = route.query.id as string; routeId = route.query.id as string;
getHouseMain(routeid.value).then((res) => { getHouseMain(routeId).then((res) => {
HouseInfo.value = res.data; HouseInfo.value = res.data;
getBillByHouse(res.data.house);
}); });
} }
} }
init(); init();
const billList = ref<billlistType[]>([]);
const queryParams = ref({
pageSize: 10,
pageNum: 1
});
const total = ref(0);
function getBillByHouse(info: House) {
const obj = {
buildingId: info.buildingId,
houseId: info.houseId,
pageNum: queryParams.value.pageNum,
pageSize: queryParams.value.pageSize,
unitNoId: info.unitNoId,
payStatus: null,
villageId: info.villageId
};
billQuery(obj).then((res) => {
billList.value = res.rows;
total.value = res.total;
});
}
function getList() {
if (HouseInfo.value) {
getBillByHouse(HouseInfo.value.house);
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -300,10 +357,10 @@ init();
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr);
&.remarkbuildingformbody { &.remarkBuildingFormBody {
display: block; display: block;
} }
.descriptionsbox { .descriptionsBox {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 10px 0; margin: 10px 0;

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="UnitBox"> <div class="UnitBox">
<div class="Unit_top flex align-center justify-between"> <div class="Unit_top flex align-center justify-between flex-wrap">
<div style="text-wrap: nowrap"> <div style="text-wrap: wrap">
<span class="text-size-16px text-color-black text-widget-bold mr-30px"> <span class="text-size-16px text-color-black text-widget-bold mr-30px">
<strong>{{ houseStore.currentBuildingInfo?.buildingName }}</strong> <strong>{{ houseStore.currentBuildingInfo?.buildingName }}</strong>
</span> </span>
@@ -17,7 +17,7 @@
<span v-has-permi="['unitNo:unitNo:add']" class="mr-20px" @click="handleAddUnit">+添加单元</span> <span v-has-permi="['unitNo:unitNo:add']" class="mr-20px" @click="handleAddUnit">+添加单元</span>
<span v-has-permi="['system:house:add']" class="mr-20px" @click="handleAddHouse">+增加房屋</span> <span v-has-permi="['system:house:add']" class="mr-20px" @click="handleAddHouse">+增加房屋</span>
<!-- <span class="mr-20px">收费标准设置</span> --> <!-- <span class="mr-20px">收费标准设置</span> -->
<span v-has-permi="['system:house:remove']" @click="deleteAllhouse">批量删除房屋</span> <span v-has-permi="['system:house:remove']" @click="deleteAllHouse">批量删除房屋</span>
<el-select v-model="houseUse" class="selectbox" clearable placeholder="筛选房屋类型" @change="handleChangeUse" @clear="handleClearUse"> <el-select v-model="houseUse" class="selectbox" clearable placeholder="筛选房屋类型" @change="handleChangeUse" @clear="handleClearUse">
<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" />
</el-select> </el-select>
@@ -200,7 +200,7 @@ function reset() {
houseStore.getHouseBybuildingNo(housename.value, houseUse.value); houseStore.getHouseBybuildingNo(housename.value, houseUse.value);
} }
async function deleteAllhouse() { async function deleteAllHouse() {
await proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗?'); await proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗?');
const checkhouse = houseStore.checkoutHouses; const checkhouse = houseStore.checkoutHouses;
if (checkhouse.length > 0) { if (checkhouse.length > 0) {
@@ -229,12 +229,16 @@ $primary_color: #1978fe;
min-height: 0; min-height: 0;
.Unit_top { .Unit_top {
padding: 20px; padding: 20px 20px 0;
height: 60px; flex-wrap: wrap;
box-sizing: border-box;
color: rgba(102, 102, 102, 1); color: rgba(102, 102, 102, 1);
& > div {
margin-bottom: 10px;
}
.rightActions { .rightActions {
color: $primary_color; color: $primary_color;
height: 35px; height: 25px;
span { span {
cursor: pointer; cursor: pointer;
} }
@@ -245,17 +249,17 @@ $primary_color: #1978fe;
width: 120px; width: 120px;
} }
@media (max-width: 1400px) { @media (max-width: 1400px) {
width: 80px; width: 100px;
} }
} }
.searchbox { s .searchbox {
.searchinput { .searchinput {
width: 250px; width: 250px;
@media (max-width: 1550px) { @media (max-width: 1550px) {
width: 130px; width: 130px;
} }
@media (max-width: 1400px) { @media (max-width: 1400px) {
width: 50px; width: 80px;
} }
} }
} }

View File

@@ -117,6 +117,7 @@
<el-form-item label="租赁起始日" prop="leaseStartDate"> <el-form-item label="租赁起始日" prop="leaseStartDate">
<el-date-picker <el-date-picker
v-model.trim="form.leaseStartDate" v-model.trim="form.leaseStartDate"
:disabled-date="disabledStartDate"
format="YYYY-MM-DD" format="YYYY-MM-DD"
placeholder="请选择租赁起始日" placeholder="请选择租赁起始日"
style="width: 100%" style="width: 100%"
@@ -127,6 +128,7 @@
<el-form-item label="租赁终止日" prop="leaseEndDate"> <el-form-item label="租赁终止日" prop="leaseEndDate">
<el-date-picker <el-date-picker
v-model.trim="form.leaseEndDate" v-model.trim="form.leaseEndDate"
:disabled-date="disabledEndDate"
format="YYYY-MM-DD" format="YYYY-MM-DD"
placeholder="请选择租赁终止日" placeholder="请选择租赁终止日"
style="width: 100%" style="width: 100%"
@@ -306,6 +308,33 @@ const data = reactive<PageData<HouseRentalFormType, HouseRentalQuery>>({
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 获取今日零点时间戳 */
const todayStart = new Date();
todayStart.setHours(0, 0, 0, 0);
const TODAY = todayStart.getTime();
/**
* 起始日禁用规则:
* 1. 今日之前不可选
* 2. 若已选结束日,则结束日之后不可选
*/
function disabledStartDate(date: Date): boolean {
if (date.getTime() < TODAY) return true;
if (!form.value.leaseEndDate) return false;
return date.getTime() > new Date(form.value.leaseEndDate).getTime();
}
/**
* 结束日禁用规则:
* 1. 今日之前不可选
* 2. 若已选起始日,则起始日之前不可选
*/
function disabledEndDate(date: Date): boolean {
if (date.getTime() < TODAY) return true;
if (!form.value.leaseStartDate) return false;
return date.getTime() < new Date(form.value.leaseStartDate).getTime();
}
/** 查询房屋租赁管理列表 */ /** 查询房屋租赁管理列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
@@ -455,6 +484,12 @@ const handleExceed = () => {
ElMessage.warning(`最多只能上传 ${MAX_UPLOAD_FILES} 个附件`); ElMessage.warning(`最多只能上传 ${MAX_UPLOAD_FILES} 个附件`);
}; };
const handleChange = (file: UploadUserFile) => { const handleChange = (file: UploadUserFile) => {
// 过滤上传文件-只允许图片,pdf格式
if (!/\.(jpg|jpeg|png|gif|pdf)$/i.test(file.name)) {
ElMessage.warning('只允许上传图片,pdf格式');
uploadRef.value?.handleRemove(file);
return;
}
// 实时判断当前已存在图片总数 // 实时判断当前已存在图片总数
if (showImgList.value.length > MAX_UPLOAD_FILES) { if (showImgList.value.length > MAX_UPLOAD_FILES) {
ElMessage.warning(`最多上传${MAX_UPLOAD_FILES}个附件,无法继续添加`); ElMessage.warning(`最多上传${MAX_UPLOAD_FILES}个附件,无法继续添加`);
@@ -463,6 +498,7 @@ const handleChange = (file: UploadUserFile) => {
return; return;
} }
}; };
// 批量上传校验图片数量,前后端数量匹配校验 // 批量上传校验图片数量,前后端数量匹配校验
const uploadAllCarousel = async () => { const uploadAllCarousel = async () => {
const filterArr = showImgList.value.filter((item) => { const filterArr = showImgList.value.filter((item) => {

View File

@@ -8,6 +8,7 @@
<el-form-item label="选择日期" prop="statDate"> <el-form-item label="选择日期" prop="statDate">
<el-date-picker <el-date-picker
v-model="starDate" v-model="starDate"
clearable
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY/MM/DD" format="YYYY/MM/DD"
range-separator="到" range-separator="到"
@@ -15,6 +16,7 @@
type="daterange" type="daterange"
unlink-panels unlink-panels
value-format="YYYY/MM/DD" value-format="YYYY/MM/DD"
@clear="handleClear"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -28,22 +30,19 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10"> <div class="flex justify-between items-center">
<el-col :span="5">
<span style="font-size: 1.2rem">日报列表</span> <span style="font-size: 1.2rem">日报列表</span>
</el-col> <div>
<el-col :span="14"></el-col>
<el-col :span="5" style="text-align: right">
<el-button type="primary" @click="exportChecked">导出选中</el-button> <el-button type="primary" @click="exportChecked">导出选中</el-button>
<el-button type="primary" @click="exportCurrentPage">导出当前页</el-button> <el-button type="primary" @click="exportCurrentPage">导出当前页</el-button>
<el-button type="primary" @click="exportAll">导出所有</el-button> <el-button type="primary" @click="exportAll">导出所有</el-button>
</el-col> </div>
</el-row> </div>
</template> </template>
<el-table v-loading="loading" :data="itemList" border row-key="id" @selectionChange="handleChange"> <el-table v-loading="loading" :data="itemList" border row-key="id" @selectionChange="handleChange">
<el-table-column type="selection" width="55" /> <el-table-column type="selection" width="55" />
<el-table-column align="center" label="日期" prop="statDate" width="100" /> <el-table-column align="center" label="日期" prop="statDate" width="100" />
<el-table-column align="center" label="计划巡检任务" prop="totalPlans" width="120" /> <el-table-column align="center" label="计划巡检任务" prop="totalTasks" width="120" />
<el-table-column align="center" label="按时完成" prop="completedTasks" /> <el-table-column align="center" label="按时完成" prop="completedTasks" />
<el-table-column align="center" label="超时完成" prop="outCompletedTasks" /> <el-table-column align="center" label="超时完成" prop="outCompletedTasks" />
<el-table-column align="center" label="超时未完成" prop="outUncompletedTasks" /> <el-table-column align="center" label="超时未完成" prop="outUncompletedTasks" />
@@ -78,13 +77,18 @@ const data = reactive({
} }
}); });
const starDate = ref([]); const starDate = ref([]);
function handleClear() {
starDate.value = [];
queryParams.value.queryDateBegin = undefined;
queryParams.value.queryDateEnd = undefined;
}
const { queryParams } = toRefs(data); const { queryParams } = toRefs(data);
/** 查询巡检项目列表 */ /** 查询巡检项目列表 */
const getList = async () => { const getList = async () => {
queryParams.value.statDate = starDate.value.join('-'); queryParams.value.statDate = starDate.value.join('-');
queryParams.value.queryDateBegin = starDate.value[0]; queryParams.value.queryDateBegin = starDate.value[0] || undefined;
queryParams.value.queryDateEnd = starDate.value[1]; queryParams.value.queryDateEnd = starDate.value[1] || undefined;
loading.value = true; loading.value = true;
listItem(queryParams.value) listItem(queryParams.value)
.then((res) => { .then((res) => {

View File

@@ -47,9 +47,9 @@
></el-switch> ></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="添加人" prop="createByName" width="120" /> <el-table-column align="center" label="添加人" prop="createByName" width="150" />
<el-table-column align="center" label="添加时间" prop="createTime" width="120" /> <el-table-column align="center" label="添加时间" prop="createTime" width="180" />
<el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作" width="300"> <el-table-column align="center" class-name="small-padding fixed-width" label="操作">
<template #default="scope"> <template #default="scope">
<el-button v-has-permi="['inspection:item:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button> <el-button v-has-permi="['inspection:item:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button v-has-permi="['inspection:item:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button> <el-button v-has-permi="['inspection:item:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
@@ -63,14 +63,13 @@
</template> </template>
<script lang="ts" name="Item" setup> <script lang="ts" name="Item" setup>
import { listItem, delItem, addItem, updateItem, updateItemStatus } from '@/api/system/InspectionItem/index'; import { listItem, delItem, updateItemStatus } from '@/api/system/InspectionItem';
import { ItemVO, ItemQuery, ItemForm } from '@/api/system/InspectionItem/type'; import { ItemVO, ItemQuery, ItemForm } from '@/api/system/InspectionItem/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const itemList = ref<ItemVO[]>([]); const itemList = ref<ItemVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const ids = ref<Array<string | number>>([]); const ids = ref<Array<string | number>>([]);
@@ -79,12 +78,6 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const itemFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ItemForm = { const initFormData: ItemForm = {
id: undefined, id: undefined,
@@ -123,7 +116,7 @@ const data = reactive<PageData<ItemForm, ItemQuery>>({
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams } = toRefs(data);
/** 查询巡检项目列表 */ /** 查询巡检项目列表 */
const getList = async () => { const getList = async () => {
@@ -138,18 +131,6 @@ const getList = async () => {
}); });
}; };
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
itemFormRef.value?.resetFields();
};
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
@@ -179,7 +160,7 @@ const handleAdd = () => {
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: ItemVO) => { const handleUpdate = (row?: ItemVO) => {
const _ids = row?.id || ids.value[0]; const _ids = row?.id || ids.value[0];
router.push({ router.push({
path: '/inspection/editItem', path: '/inspection/editItem',
@@ -204,23 +185,6 @@ const handleChange = (value: string, id: string) => {
}); });
}; };
/** 提交按钮 */
const submitForm = () => {
itemFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateItem(form.value).finally(() => (buttonLoading.value = false));
} else {
await addItem(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: ItemVO) => { const handleDelete = async (row?: ItemVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
@@ -230,17 +194,6 @@ const handleDelete = async (row?: ItemVO) => {
await getList(); await getList();
}; };
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'system/item/export',
{
...queryParams.value
},
`item_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}); });

View File

@@ -8,6 +8,7 @@
<el-form-item label="选择日期" prop="itemName"> <el-form-item label="选择日期" prop="itemName">
<el-date-picker <el-date-picker
v-model="starDate" v-model="starDate"
clearable
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY/MM/DD" format="YYYY/MM/DD"
range-separator="到" range-separator="到"
@@ -15,6 +16,7 @@
type="daterange" type="daterange"
unlink-panels unlink-panels
value-format="YYYY/MM/DD" value-format="YYYY/MM/DD"
@clear="handleClear"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -28,23 +30,20 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10"> <div class="flex justify-between items-center">
<el-col :span="5">
<span style="font-size: 1.2rem">月报列表</span> <span style="font-size: 1.2rem">月报列表</span>
</el-col> <div>
<el-col :span="14"> </el-col>
<el-col :span="5" style="text-align: right">
<el-button type="primary" @click="exportCurrentPage">导出当前页</el-button> <el-button type="primary" @click="exportCurrentPage">导出当前页</el-button>
<el-button type="primary" @click="exportAll">导出所有</el-button> <el-button type="primary" @click="exportAll">导出所有</el-button>
<el-button type="primary" @click="exportChecked">导出选中</el-button> <el-button type="primary" @click="exportChecked">导出选中</el-button>
</el-col> </div>
</el-row> </div>
</template> </template>
<el-table v-loading="loading" :data="itemList" border @selectionChange="handleChange"> <el-table v-loading="loading" :data="itemList" border @selectionChange="handleChange">
<el-table-column type="selection" width="55" /> <el-table-column type="selection" width="55" />
<el-table-column align="center" label="日期" prop="statDate" width="100" /> <el-table-column align="center" label="日期" prop="statDate" width="100" />
<el-table-column align="center" label="计划巡检任务" prop="totalPlans" width="120" /> <el-table-column align="center" label="计划巡检任务" prop="totalTasks" width="120" />
<el-table-column align="center" label="按时完成" prop="completedTasks" /> <el-table-column align="center" label="按时完成" prop="completedTasks" />
<el-table-column align="center" label="超时完成" prop="outCompletedTasks" /> <el-table-column align="center" label="超时完成" prop="outCompletedTasks" />
<el-table-column align="center" label="超时未完成" prop="outUncompletedTasks" /> <el-table-column align="center" label="超时未完成" prop="outUncompletedTasks" />
@@ -67,6 +66,11 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const starDate = ref([]); const starDate = ref([]);
function handleClear() {
starDate.value = [];
queryParams.value.queryDateBegin = undefined;
queryParams.value.queryDateEnd = undefined;
}
const data = reactive({ const data = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
@@ -83,8 +87,8 @@ const { queryParams } = toRefs(data);
/** 查询巡检项目列表 */ /** 查询巡检项目列表 */
const getList = async () => { const getList = async () => {
queryParams.value.statDate = starDate.value.join('-'); queryParams.value.statDate = starDate.value.join('-');
queryParams.value.queryDateBegin = starDate.value[0]; queryParams.value.queryDateBegin = starDate.value[0] || undefined;
queryParams.value.queryDateEnd = starDate.value[1]; queryParams.value.queryDateEnd = starDate.value[1] || undefined;
loading.value = true; loading.value = true;
listItem(queryParams.value) listItem(queryParams.value)
.then((res) => { .then((res) => {

View File

@@ -296,7 +296,7 @@ const getList = async () => {
}); });
list.value[3].value = itemList.value[0].completionRateDisplay || '0%'; list.value[3].value = itemList.value[0].completionRateDisplay || '0%';
itemList.value.forEach((item) => { itemList.value.forEach((item) => {
list.value[0].value += item.totalPlans ?? 0; list.value[0].value = item.villagePlanCount ?? 0;
list.value[1].value += item.totalTasks ?? 0; list.value[1].value += item.totalTasks ?? 0;
list.value[4].value += item.outUncompletedTasks ?? 0; list.value[4].value += item.outUncompletedTasks ?? 0;
const num = item.outCompletedTasks + item.completedTasks || 0; const num = item.outCompletedTasks + item.completedTasks || 0;

View File

@@ -103,10 +103,10 @@ const checkoutWeek = ref([]);
function checkoutWeekFun(val: number) { function checkoutWeekFun(val: number) {
if (!checkoutWeek.value.includes(val)) { if (!checkoutWeek.value.includes(val)) {
checkoutWeek.value.push(val); checkoutWeek.value.push(val);
form.value.executeDay = checkoutWeek.value.join(',');
} else { } else {
checkoutWeek.value = checkoutWeek.value.filter((item) => item !== val); checkoutWeek.value = checkoutWeek.value.filter((item) => item !== val);
} }
form.value.executeDay = checkoutWeek.value.join(',');
} }
// 提醒时间 // 提醒时间

View File

@@ -118,8 +118,7 @@ const data = reactive<PageData<PlanForm, PlanQuery>>({
inspectSetting: undefined, inspectSetting: undefined,
routeIds: undefined, routeIds: undefined,
status: undefined, status: undefined,
executeDay: undefined, executeDay: undefined
params: {}
}, },
rules: {} rules: {}
}); });

View File

@@ -71,35 +71,51 @@
> >
<el-table-column align="center" label="序号" type="index" width="85" /> <el-table-column align="center" label="序号" type="index" width="85" />
<el-table-column align="center" label="巡检点" prop="pointName" /> <el-table-column align="center" label="巡检点" prop="pointName" />
<el-table-column align="center" label=" 巡检时间" prop="checkTime" sortable /> <el-table-column align="center" label=" 巡检时间" prop="checkTime">
<template #default="scope">
<span v-if="scope.row.checkTime !== null">{{ scope.row.checkTime }}</span>
<span v-else>---</span>
</template>
</el-table-column>
<el-table-column align="center" label="巡检状态" prop="status"> <el-table-column align="center" label="巡检状态" prop="status">
<template #default="scope"> <template #default="scope">
<div v-if="scope.row.status !== null">
<el-tag v-if="scope.row.status === 0" type="success">正常</el-tag> <el-tag v-if="scope.row.status === 0" type="success">正常</el-tag>
<el-tag v-else type="danger">异常</el-tag> <el-tag v-else type="danger">异常</el-tag>
</div>
<div v-else>---</div>
</template> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column prop="location" align="center" label="巡检地址" /> --> <!-- <el-table-column prop="location" align="center" label="巡检地址" /> -->
<el-table-column align="left" label="巡检结果" prop="remark" /> <el-table-column align="left" label="巡检结果" prop="remark">
<el-table-column label="图片">
<template #default="scope"> <template #default="scope">
<div> <div v-if="scope.row.remark !== null">
{{ scope.row.remark }}
</div>
<div v-else>---</div>
</template>
</el-table-column>
<el-table-column align="center" label="图片" width="220">
<template #default="scope">
<div v-if="scope.row.photoslist" class="showImages">
<el-image <el-image
v-for="(item, index) in scope.row.photoslist" v-for="(item, index) in scope.row.photoslist"
:key="index" :key="index"
:initial-index="0" :initial-index="index"
:preview-src-list="scope.row.photoslist" :preview-src-list="scope.row.photoslist"
:preview-teleported="true" :preview-teleported="true"
:src="item" :src="item"
fit="cover" fit="cover"
show-progress show-progress
style="width: 80px; height: 50px; margin-right: 10px"
/> />
</div> </div>
<div v-else>---</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="left" label="执行结果" prop="completionStatus"> <el-table-column align="left" label="执行结果" prop="completionStatus">
<template #default="scope"> <template #default="scope">
<DictTag :options="com_inspection_task_status" :value="scope.row.completionStatus"></DictTag> <el-tag v-if="scope.row.completionStatus === null" type="danger">未完成</el-tag>
<DictTag v-else :options="com_inspection_task_status" :value="scope.row.completionStatus"></DictTag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -273,4 +289,19 @@ const cancelForm = () => {
margin-bottom: 20px; margin-bottom: 20px;
} }
} }
.showImages {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 5px;
.el-image {
width: 50px;
height: 30px;
object-fit: cover;
}
//display: flex;
//align-items: center;
//justify-content: center;
//flex-wrap: wrap;
}
</style> </style>

View File

@@ -13,12 +13,14 @@
<el-form-item label="巡检日期" prop="taskTime"> <el-form-item label="巡检日期" prop="taskTime">
<el-date-picker <el-date-picker
v-model="taskTime" v-model="taskTime"
clearable
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY-MM-DD" format="YYYY-MM-DD"
range-separator="到" range-separator="到"
start-placeholder="开始日期" start-placeholder="开始日期"
type="daterange" type="daterange"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@clear="handleClear"
@update:model-value="handleDate" @update:model-value="handleDate"
/> />
</el-form-item> </el-form-item>
@@ -69,7 +71,7 @@
</template> </template>
<script lang="ts" name="Record" setup> <script lang="ts" name="Record" setup>
import { listRecord, delRecord, addRecord, updateRecord } from '@/api/system/InspectionRecord/index'; import { listRecord } from '@/api/system/InspectionRecord';
import { RecordVO, RecordQuery, RecordForm } from '@/api/system/InspectionRecord/type'; import { RecordVO, RecordQuery, RecordForm } from '@/api/system/InspectionRecord/type';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -84,12 +86,6 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const recordFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: RecordForm = { const initFormData: RecordForm = {
id: undefined, id: undefined,
@@ -115,7 +111,7 @@ const data = reactive<PageData<RecordForm, RecordQuery>>({
} }
}); });
const { queryParams, form } = toRefs(data); const { queryParams } = toRefs(data);
/** 查询巡检记录主列表 */ /** 查询巡检记录主列表 */
const getList = async () => { const getList = async () => {
@@ -132,21 +128,15 @@ const getList = async () => {
// 处理日期选择 // 处理日期选择
const taskTime = ref([]); const taskTime = ref([]);
const handleDate = (val) => { function handleClear() {
queryParams.value.taskTimeBegin = '';
queryParams.value.taskTimeEnd = '';
}
const handleDate = (val: string[]) => {
if (val !== null) {
queryParams.value.taskTimeBegin = val[0]; queryParams.value.taskTimeBegin = val[0];
queryParams.value.taskTimeEnd = val[1]; queryParams.value.taskTimeEnd = val[1];
}; }
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
recordFormRef.value?.resetFields();
}; };
/** 搜索按钮操作 */ /** 搜索按钮操作 */

View File

@@ -5,9 +5,10 @@
<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" :inline="true" label-width="120px"> <el-form ref="queryFormRef" :inline="true" label-width="120px">
<el-form-item label="选择日期" prop="checkdata"> <el-form-item label="选择日期" prop="checkData">
<el-date-picker <el-date-picker
v-model="checkdata" v-model="checkData"
clearable
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY/MM/DD" format="YYYY/MM/DD"
placeholder="选择日期" placeholder="选择日期"
@@ -15,6 +16,7 @@
start-placeholder="开始日期" start-placeholder="开始日期"
type="daterange" type="daterange"
value-format="YYYY/MM/DD" value-format="YYYY/MM/DD"
@clear="handleClear"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -75,7 +77,12 @@ const data = reactive({
} }
}); });
const checkdata = ref([]); const checkData = ref([]);
function handleClear() {
checkData.value = [];
queryParams.value.queryDateBegin = undefined;
queryParams.value.queryDateEnd = undefined;
}
const { queryParams } = toRefs(data); const { queryParams } = toRefs(data);
/** 查询巡检路线列表*/ /** 查询巡检路线列表*/
const getList = async () => { const getList = async () => {
@@ -92,14 +99,17 @@ const getList = async () => {
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.queryDateBegin = checkdata.value[0]; queryParams.value.queryDateBegin = checkData.value[0] || undefined;
queryParams.value.queryDateEnd = checkdata.value[1]; queryParams.value.queryDateEnd = checkData.value[1] || undefined;
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
}; };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryParams.value.queryDateBegin = '';
queryParams.value.queryDateEnd = '';
checkData.value = [];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
}; };

View File

@@ -6,28 +6,28 @@
<div>基本信息</div> <div>基本信息</div>
</div> </div>
<div class="bodyBox"> <div class="bodyBox">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" label-position="right"> <el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="120px">
<el-form-item label="任务名称" prop="taskName"> <el-form-item label="任务名称" prop="taskName">
<el-input maxlength="20" v-model="form.taskName" placeholder="请输入"></el-input> <el-input v-model="form.taskName" maxlength="20" placeholder="请输入"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="巡检人员" prop="inspectorId"> <el-form-item label="巡检人员" prop="inspectorId">
<div class="inspectorIdsBox"> <div class="inspectorIdsBox">
<div v-for="(item, index) in checkoutUserlist" :key="index" class="inspector_items"> <div v-for="(item, index) in checkoutUserlist" :key="index" class="inspector_items">
<div class="name">{{ item.nickName }}</div> <div class="name">{{ item.nickName }}</div>
<i @click="handleDelete(item.userId)" class="deleteAction"> <i class="deleteAction" @click="handleDelete(item.userId)">
<el-icon><Delete /></el-icon> <el-icon><Delete /></el-icon>
</i> </i>
</div> </div>
<RepairUser <RepairUser
titleName="巡检人员"
@change="handleUserChange"
returnvalue="*"
:is-multiple="true"
user-type="inspection"
ref="RepairUserRef" ref="RepairUserRef"
:is-multiple="true"
returnvalue="*"
titleName="巡检人员"
user-type="inspection"
@change="handleUserChange"
> >
<template #default> <template #default>
<div @click="handleAddUser" class="inspector_items add flex flex-items-center justify-center"> <div class="inspector_items add flex flex-items-center justify-center" @click="handleAddUser">
<el-icon><Plus /></el-icon> <el-icon><Plus /></el-icon>
<span class="ml-10px"> 添加 </span> <span class="ml-10px"> 添加 </span>
</div> </div>
@@ -37,10 +37,13 @@
</el-form-item> </el-form-item>
<el-form-item label="巡更日期" prop="taskTime"> <el-form-item label="巡更日期" prop="taskTime">
<el-date-picker <el-date-picker
v-model="taskTime"
end-placeholder="结束日期"
format="YYYY-MM-DD"
range-separator="到" range-separator="到"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" type="daterange"
v-model="taskTime" value-format="YYYY-MM-DD"
@update:model-value=" @update:model-value="
(val) => { (val) => {
if (val.length > 0) { if (val.length > 0) {
@@ -48,9 +51,6 @@
} }
} }
" "
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/> />
</el-form-item> </el-form-item>
<el-form-item label="提醒时间" prop="remindTime"> <el-form-item label="提醒时间" prop="remindTime">
@@ -59,12 +59,12 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="关联计划" prop="planId"> <el-form-item label="关联计划" prop="planId">
<el-select @change="handleChangeMap" v-model="form.planId" placeholder="请选择" style="width: 100%"> <el-select v-model="form.planId" placeholder="请选择" style="width: 100%" @change="handleChangeMap">
<el-option v-for="item in planList" :disabled="item.status === '1'" :key="item.id" :label="item.planName" :value="item.id" /> <el-option v-for="item in planList" :key="item.id" :disabled="item.status === '1'" :label="item.planName" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="计划路线" prop="planId"> <el-form-item label="计划路线" prop="planId">
<MapWrapper v-loading="mapLoading" :disabled="true" ref="MapWrapperRef"></MapWrapper> <MapWrapper ref="MapWrapperRef" v-loading="mapLoading" :disabled="true"></MapWrapper>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="submit">提交</el-button> <el-button type="primary" @click="submit">提交</el-button>
@@ -76,7 +76,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import RepairUser from '@/components/Holder/repairUser.vue'; import RepairUser from '@/components/Holder/repairUser.vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { Delete, Plus } from '@element-plus/icons-vue'; import { Delete, Plus } from '@element-plus/icons-vue';
@@ -157,6 +157,7 @@ const mapLoading = ref(false);
const MapWrapperRef = ref<InstanceType<typeof MapWrapper>>(); const MapWrapperRef = ref<InstanceType<typeof MapWrapper>>();
let planWeek = '';
function handleChangeMap(val: string) { function handleChangeMap(val: string) {
mapLoading.value = true; mapLoading.value = true;
const find = planList.value.find((item) => item.id === val); const find = planList.value.find((item) => item.id === val);
@@ -185,8 +186,10 @@ function handleChangeMap(val: string) {
} else { } else {
mapLoading.value = false; mapLoading.value = false;
} }
planWeek = find.executeDay || '';
} else { } else {
mapLoading.value = false; mapLoading.value = false;
planWeek = '';
} }
} }
@@ -201,6 +204,13 @@ function submit() {
} else { } else {
form.value.taskTime = ''; form.value.taskTime = '';
} }
if (planWeek) {
const result = hasDateWeekIntersection(taskTime.value[0], taskTime.value[1], planWeek);
if (!result) {
ElMessage.warning('所选【巡更日期】与【选定计划中执行星期】无重合,请重新选择');
return;
}
}
form.value.inspectorId = checkoutUserlist.value.map((item) => item.userId).join(','); form.value.inspectorId = checkoutUserlist.value.map((item) => item.userId).join(',');
formRef.value?.validate(async (valid: boolean) => { formRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
@@ -218,9 +228,39 @@ function submit() {
} }
}); });
} }
/**
* @param start "2026-09-11"
* @param end "2026-09-15"
* @param weekStr "1,2,3" 1周一,2周二,3周三...0周日
* @returns boolean 是否存在
*/
function hasDateWeekIntersection(start: string, end: string, weekStr: string): boolean {
const allowWeek = weekStr
.split(',')
.map(Number)
.sort((a, b) => a - b);
const startDate = new Date(start);
const endDate = new Date(end);
// 循环 可执行星期
for (const w of allowWeek) {
const startWeek = startDate.getDay();
// 计算从start往后到该星期需要的天数偏移
const offset = (w - startWeek + 7) % 7;
// 判断开始日期到最近的 可执行星期 是否在日期范围内
const target = new Date(startDate);
target.setDate(target.getDate() + offset);
if (target <= endDate) {
return true;
}
}
return false;
}
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.EditBody { .EditBody {
margin-top: 20px; margin-top: 20px;
padding: 30px; padding: 30px;

View File

@@ -13,6 +13,7 @@
<el-form-item label="巡检日期" prop="taskTime"> <el-form-item label="巡检日期" prop="taskTime">
<el-date-picker <el-date-picker
v-model="taskTime" v-model="taskTime"
clearable
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY-MM-DD" format="YYYY-MM-DD"
range-separator="到" range-separator="到"
@@ -136,8 +137,10 @@ const getList = async () => {
}; };
const taskTime = ref([]); const taskTime = ref([]);
function handleDate(data: string[]) { function handleDate(data: string[]) {
if (data !== null) {
queryParams.value.taskTimeBegin = data[0]; queryParams.value.taskTimeBegin = data[0];
queryParams.value.taskTimeEnd = data[1]; queryParams.value.taskTimeEnd = data[1];
}
} }
function handleClear() { function handleClear() {
queryParams.value.taskTimeBegin = ''; queryParams.value.taskTimeBegin = '';
@@ -153,10 +156,10 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.taskTimeBegin = ''; queryParams.value.taskTimeBegin = '';
queryParams.value.taskTimeEnd = ''; queryParams.value.taskTimeEnd = '';
taskTime.value = []; taskTime.value = [];
queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
}; };

View File

@@ -8,6 +8,7 @@
<el-form-item label="选择日期" prop="itemName"> <el-form-item label="选择日期" prop="itemName">
<el-date-picker <el-date-picker
v-model="starDate" v-model="starDate"
clearable
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY/MM/DD" format="YYYY/MM/DD"
range-separator="到" range-separator="到"
@@ -15,6 +16,7 @@
type="daterange" type="daterange"
unlink-panels unlink-panels
value-format="YYYY/MM/DD" value-format="YYYY/MM/DD"
@clear="handleClear"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -28,23 +30,20 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10"> <div class="flex justify-between items-center">
<el-col :span="5">
<span style="font-size: 1.2rem">周报列表</span> <span style="font-size: 1.2rem">周报列表</span>
</el-col> <div>
<el-col :span="14"> </el-col>
<el-col :span="5" style="text-align: right">
<el-button type="primary" @click="exportCurrentPage">导出当前页</el-button> <el-button type="primary" @click="exportCurrentPage">导出当前页</el-button>
<el-button type="primary" @click="exportAll">导出所有</el-button> <el-button type="primary" @click="exportAll">导出所有</el-button>
<el-button type="primary" @click="exportChecked">导出选中</el-button> <el-button type="primary" @click="exportChecked">导出选中</el-button>
</el-col> </div>
</el-row> </div>
</template> </template>
<el-table v-loading="loading" :data="itemList" border @selectionChange="handleChange"> <el-table v-loading="loading" :data="itemList" border @selectionChange="handleChange">
<el-table-column type="selection" width="55" /> <el-table-column type="selection" width="55" />
<el-table-column align="center" label="日期" prop="statDate" width="120" /> <el-table-column align="center" label="日期" prop="statDate" width="120" />
<el-table-column align="center" label="计划巡检任务" prop="totalPlans" width="120" /> <el-table-column align="center" label="计划巡检任务" prop="totalTasks" width="120" />
<el-table-column align="center" label="按时完成" prop="completedTasks" /> <el-table-column align="center" label="按时完成" prop="completedTasks" />
<el-table-column align="center" label="超时完成" prop="outCompletedTasks" /> <el-table-column align="center" label="超时完成" prop="outCompletedTasks" />
<el-table-column align="center" label="超时未完成" prop="outUncompletedTasks" /> <el-table-column align="center" label="超时未完成" prop="outUncompletedTasks" />
@@ -79,13 +78,18 @@ const data = reactive({
} }
}); });
const starDate = ref([]); const starDate = ref([]);
function handleClear() {
starDate.value = [];
queryParams.value.queryDateBegin = undefined;
queryParams.value.queryDateEnd = undefined;
}
const { queryParams } = toRefs(data); const { queryParams } = toRefs(data);
/** 查询巡检项目列表 */ /** 查询巡检项目列表 */
const getList = async () => { const getList = async () => {
queryParams.value.statDate = starDate.value.join('-'); queryParams.value.statDate = starDate.value.join('-');
queryParams.value.queryDateBegin = starDate.value[0]; queryParams.value.queryDateBegin = starDate.value[0] || undefined;
queryParams.value.queryDateEnd = starDate.value[1]; queryParams.value.queryDateEnd = starDate.value[1] || undefined;
loading.value = true; loading.value = true;
listItem(queryParams.value) listItem(queryParams.value)
.then((res) => { .then((res) => {

View File

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

View File

@@ -168,6 +168,13 @@ const rules = ref({
if (Number.isNaN(val)) { if (Number.isNaN(val)) {
return callback(new Error('请输入数字')); return callback(new Error('请输入数字'));
} }
if (val === 0) {
//不能输入0
return callback(new Error('请输入不等于0的数字'));
}
if (val % 1 !== 0) {
return callback(new Error('请输入整数'));
}
return callback(); return callback();
} }
} }
@@ -190,7 +197,7 @@ const rules = ref({
{ {
validator: (_: any, value: string | number, callback: (arg0?: Error) => void) => { validator: (_: any, value: string | number, callback: (arg0?: Error) => void) => {
if (value === '' || value === null) { if (value === '' || value === null) {
return callback(new Error('请输入车位面积')); return callback();
} }
const val = Number(value); const val = Number(value);
if (Number.isNaN(val)) { if (Number.isNaN(val)) {
@@ -223,12 +230,13 @@ if (route.query.id) {
} }
// ! 获取区域================================================================================== // ! 获取区域==================================================================================
const areaList = ref([]); const areaList = ref([]);
const getAreaList = async () => { const getAreaList = () => {
const res = await listArea({ listArea({
pageSize: 99999, pageSize: 99999,
pageNum: 1 pageNum: 1
}); }).then((res) => {
areaList.value = res.rows; areaList.value = res.rows;
});
}; };
getAreaList(); getAreaList();

View File

@@ -4,19 +4,19 @@
<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" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" @submit.prevent="handleQuery">
<el-form-item label="车位状态" prop="parkingStatus"> <el-form-item label="车位状态" prop="parkingStatus">
<el-select v-model="queryParams.parkingStatus" placeholder="请选择" clearable> <el-select v-model="queryParams.parkingStatus" clearable placeholder="请选择">
<el-option v-for="dict in com_parking_status" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in com_parking_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="审核状态" prop="checkStatus"> <el-form-item label="审核状态" prop="checkStatus">
<el-select v-model="queryParams.checkStatus" placeholder="请选择" clearable> <el-select v-model="queryParams.checkStatus" clearable placeholder="请选择">
<el-option v-for="dict in com_review" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in com_review" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="车位编号" prop="parkingCode"> <el-form-item label="车位编号" prop="parkingCode">
<el-input maxlength="20" v-model="queryParams.parkingCode" placeholder="请输入车位编号" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.parkingCode" clearable maxlength="20" placeholder="请输入车位编号" @keyup.enter="handleQuery" />
</el-form-item> </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>
@@ -34,8 +34,8 @@
:disable-delete="multiple" :disable-delete="multiple"
:disable-edit="single" :disable-edit="single"
:show-add="checkPermi(['parking:parking:add'])" :show-add="checkPermi(['parking:parking:add'])"
:show-edit="checkPermi(['parking:parking:edit'])"
:show-delete="checkPermi(['parking:parking:remove'])" :show-delete="checkPermi(['parking:parking:remove'])"
:show-edit="checkPermi(['parking:parking:edit'])"
@update:add="handleAdd" @update:add="handleAdd"
@update:edit="handleUpdate()" @update:edit="handleUpdate()"
@update:delete="handleDelete()" @update:delete="handleDelete()"
@@ -43,59 +43,61 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="parkingList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="parkingList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column align="center" type="selection" width="55" />
<el-table-column label="区域" align="center" prop="areaName"> </el-table-column> <el-table-column align="center" label="区域" prop="areaName"> </el-table-column>
<el-table-column label="车位编号" align="center" prop="parkingCode" /> <el-table-column align="center" label="车位编号" prop="parkingCode" />
<el-table-column label="车位面积" align="center" prop="parkingArea"> <el-table-column align="center" label="车位面积" prop="parkingArea">
<template #default="scope"> {{ scope.row.parkingArea }}㎡ </template> <template #default="scope">
<span v-if="scope.row.parkingArea">{{ scope.row.parkingArea }} </span> <span v-if="scope.row.parkingArea">㎡</span>
</template>
</el-table-column> </el-table-column>
<el-table-column label="楼层" width="80" align="center" prop="parkingFloor" /> <el-table-column align="center" label="楼层" prop="parkingFloor" width="80" />
<el-table-column label="车位类型" align="center" prop="type"> <el-table-column align="center" label="车位类型" prop="type">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_parking_type" :value="scope.row.type" /> <dict-tag :options="com_parking_type" :value="scope.row.type" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="车位状态" width="80" align="center" prop="parkingStatus"> <el-table-column align="center" label="车位状态" prop="parkingStatus" width="80">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_parking_status" :value="scope.row.parkingStatus" /> <dict-tag :options="com_parking_status" :value="scope.row.parkingStatus" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="持有人" align="center" prop="residentName"> <el-table-column align="center" label="持有人" prop="residentName">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.residentName ?? scope.row.contactName }}</span> <span>{{ scope.row.residentName ?? scope.row.contactName }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="审核状态" align="center" prop="checkStatus"> <el-table-column align="center" label="审核状态" prop="checkStatus">
<template #default="scope"> <template #default="scope">
<dict-tag v-if="scope.row.checkStatus" :options="com_review" :value="scope.row.checkStatus" /> <dict-tag v-if="scope.row.checkStatus" :options="com_review" :value="scope.row.checkStatus" />
<span v-else>---</span> <span v-else>---</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip /> <el-table-column align="center" label="备注" prop="remark" show-overflow-tooltip />
<el-table-column label="操作" width="180" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column align="center" class-name="small-padding fixed-width" fixed="right" label="操作" width="180">
<template #default="scope"> <template #default="scope">
<!-- 待审核 审核不通过 --> <!-- 待审核 审核不通过 -->
<el-button <el-button
v-if="scope.row.checkStatus === '0' || scope.row.checkStatus === '2'"
v-has-permi="['parking:parking:query']"
link link
type="primary" type="primary"
v-if="scope.row.checkStatus === '0' || scope.row.checkStatus === '2'"
@click="handleReview(scope.row)" @click="handleReview(scope.row)"
v-has-permi="['parking:parking:query']"
>审核</el-button >审核</el-button
> >
<el-button link type="primary" v-else @click="handleMain(scope.row)">详情</el-button> <el-button v-else link type="primary" @click="handleMain(scope.row)">详情</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['parking:parking:edit']">编辑</el-button> <el-button v-has-permi="['parking:parking:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['parking:parking:remove']">删除</el-button> <el-button v-has-permi="['parking:parking:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<Pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <Pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="Parking" lang="ts"> <script lang="ts" name="Parking" setup>
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import Pagination from '@/components/Pagination/index.vue'; import Pagination from '@/components/Pagination/index.vue';
import RightToolbar from '@/components/RightToolbar/index.vue'; import RightToolbar from '@/components/RightToolbar/index.vue';

View File

@@ -2,7 +2,7 @@
<div class="ResidentMainBox p-2 w-full h-full"> <div class="ResidentMainBox p-2 w-full h-full">
<PageHeader></PageHeader> <PageHeader></PageHeader>
<div class="ResidentMainBody"> <div class="ResidentMainBody">
<el-row :span="24" :gutter="20"> <el-row :gutter="20" :span="24">
<el-col :span="24"> <el-col :span="24">
<!-- 车位信息 --> <!-- 车位信息 -->
<div class="mb-10"> <div class="mb-10">
@@ -41,13 +41,13 @@
<DictTag :options="com_parking_status" :value="parkingReviewInfo.parking.parkingStatus"></DictTag> <DictTag :options="com_parking_status" :value="parkingReviewInfo.parking.parkingStatus"></DictTag>
</div> </div>
</div> </div>
<div class="carInfo_item" v-if="parkingReviewInfo.parking.residentId"> <div v-if="parkingReviewInfo.parking.residentId" class="carInfo_item">
<div class="label">持有人:</div> <div class="label">持有人:</div>
<div class="main"> <div class="main">
<span> <span>
{{ parkingReviewInfo.parking.residentName }} {{ parkingReviewInfo.parking.residentName }}
</span> </span>
<el-button @click="getUserInfo" type="primary" link style="font-size: smaller; margin-left: 15px">详情</el-button> <el-button link style="font-size: smaller; margin-left: 15px" type="primary" @click="getUserInfo">详情</el-button>
</div> </div>
</div> </div>
<div class="carInfo_item"> <div class="carInfo_item">
@@ -60,16 +60,16 @@
</div> </div>
</div> </div>
<div class="CarInfoBox" style="margin-top: 20px"> <div class="CarInfoBox" style="margin-top: 20px">
<div class="carInfo_item" v-if="parkingReviewInfo.parking.contactName"> <div v-if="parkingReviewInfo.parking.contactName" class="carInfo_item">
<div class="label">非业主联系人:</div> <div class="label">非业主联系人:</div>
<div class="main">{{ parkingReviewInfo.parking.contactName }}</div> <div class="main">{{ parkingReviewInfo.parking.contactName }}</div>
</div> </div>
<div class="carInfo_item" style="margin-top: 20px" v-if="parkingReviewInfo.parking.contactPhone"> <div v-if="parkingReviewInfo.parking.contactPhone" class="carInfo_item" style="margin-top: 20px">
<div class="label">联系电话:</div> <div class="label">联系电话:</div>
<div class="main">{{ parkingReviewInfo.parking.contactPhone }}</div> <div class="main">{{ parkingReviewInfo.parking.contactPhone }}</div>
</div> </div>
<div class="carInfo_item" style="margin-top: 20px"> <div class="carInfo_item" style="margin-top: 20px">
<div class="label">备注:</div> <div class="label" style="min-width: 120px">备注:</div>
<div class="main">{{ parkingReviewInfo.parking.remark }}</div> <div class="main">{{ parkingReviewInfo.parking.remark }}</div>
</div> </div>
</div> </div>
@@ -83,7 +83,7 @@
<span>车辆信息</span> <span>车辆信息</span>
</div> </div>
</template> </template>
<div class="w-full h-full CarInfoBox" v-if="parkingReviewInfo.car"> <div v-if="parkingReviewInfo.car" class="w-full h-full CarInfoBox">
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<div class="carInfo_item"> <div class="carInfo_item">
@@ -127,10 +127,10 @@
<el-image <el-image
v-for="(item, index) in parkingReviewInfo.car.carImageUrlList" v-for="(item, index) in parkingReviewInfo.car.carImageUrlList"
:key="index" :key="index"
class="carInfoImage"
:src="item"
:initial-index="index" :initial-index="index"
:preview-src-list="parkingReviewInfo.car.carImageUrlList" :preview-src-list="parkingReviewInfo.car.carImageUrlList"
:src="item"
class="carInfoImage"
preview-teleported preview-teleported
></el-image> ></el-image>
</div> </div>
@@ -138,7 +138,7 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<div class="w-full h-full CarInfoBox flex flex-center min-h-17rem" v-else> <div v-else class="w-full h-full CarInfoBox flex flex-center min-h-17rem">
<span style="color: #999; font-weight: 500; font-size: larger; letter-spacing: 10px">暂无信息</span> <span style="color: #999; font-weight: 500; font-size: larger; letter-spacing: 10px">暂无信息</span>
</div> </div>
</el-card> </el-card>
@@ -147,7 +147,7 @@
</el-row> </el-row>
</div> </div>
<el-dialog width="500" @close="handleClose" v-model="MainForm.visible" title="车位持有人详情"> <el-dialog v-model="MainForm.visible" title="车位持有人详情" width="500" @close="handleClose">
<div class="item"> <div class="item">
<div class="label">姓名</div> <div class="label">姓名</div>
<div class="value">{{ MainFormInfo.name }}</div> <div class="value">{{ MainFormInfo.name }}</div>
@@ -155,13 +155,13 @@
<div class="item"> <div class="item">
<div class="label">类型</div> <div class="label">类型</div>
<div class="value"> <div class="value">
<DictTag :value="MainFormInfo.type" :options="com_resident_relationship"></DictTag> <DictTag :options="com_resident_relationship" :value="MainFormInfo.type"></DictTag>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<div class="label">状态</div> <div class="label">状态</div>
<div class="value"> <div class="value">
<DictTag :value="MainFormInfo.status" :options="com_review"></DictTag> <DictTag :options="com_review" :value="MainFormInfo.status"></DictTag>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
@@ -184,7 +184,7 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { getParkingReviewInfo, getParkingResidentInfo } from '@/api/system/parking'; import { getParkingReviewInfo, getParkingResidentInfo } from '@/api/system/parking';
@@ -351,7 +351,7 @@ getInfo();
// 物业审核状态 0 待审核 1审核通过 2审核未通过 // 物业审核状态 0 待审核 1审核通过 2审核未通过
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.ResidentMainBox { .ResidentMainBox {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -386,6 +386,7 @@ getInfo();
.label { .label {
font-weight: 600; font-weight: 600;
width: 120px; width: 120px;
min-width: 120px;
text-align: right; text-align: right;
padding-right: 10px; padding-right: 10px;
} }

View File

@@ -2,7 +2,7 @@
<div class="ResidentMainBox p-2 w-full h-full"> <div class="ResidentMainBox p-2 w-full h-full">
<PageHeader></PageHeader> <PageHeader></PageHeader>
<div class="ResidentMainBody"> <div class="ResidentMainBody">
<el-row :span="24" :gutter="20"> <el-row :gutter="20" :span="24">
<el-col :span="16"> <el-col :span="16">
<!-- 车位信息 --> <!-- 车位信息 -->
<div class="mb-10"> <div class="mb-10">
@@ -41,7 +41,7 @@
<DictTag :options="com_parking_status" :value="parkingReviewInfo.parking.parkingStatus"></DictTag> <DictTag :options="com_parking_status" :value="parkingReviewInfo.parking.parkingStatus"></DictTag>
</div> </div>
</div> </div>
<div class="carInfo_item" v-if="parkingReviewInfo.parking.residentId"> <div v-if="parkingReviewInfo.parking.residentId" class="carInfo_item">
<div class="label">持有人:</div> <div class="label">持有人:</div>
<div class="main"> <div class="main">
<span> <span>
@@ -59,30 +59,30 @@
</div> </div>
</div> </div>
<div class="CarInfoBox" style="margin-top: 20px"> <div class="CarInfoBox" style="margin-top: 20px">
<div class="carInfo_item" v-if="parkingReviewInfo.parking.contactName"> <div v-if="parkingReviewInfo.parking.contactName" class="carInfo_item">
<div class="label">非业主联系人:</div> <div class="label">非业主联系人:</div>
<div class="main">{{ parkingReviewInfo.parking.contactName }}</div> <div class="main">{{ parkingReviewInfo.parking.contactName }}</div>
</div> </div>
<div class="carInfo_item" style="margin-top: 20px" v-if="parkingReviewInfo.parking.contactPhone"> <div v-if="parkingReviewInfo.parking.contactPhone" class="carInfo_item" style="margin-top: 20px">
<div class="label">联系电话:</div> <div class="label">联系电话:</div>
<div class="main">{{ parkingReviewInfo.parking.contactPhone }}</div> <div class="main">{{ parkingReviewInfo.parking.contactPhone }}</div>
</div> </div>
<div class="carInfo_item" style="margin-top: 20px"> <div class="carInfo_item" style="margin-top: 20px">
<div class="label">备注:</div> <div class="label" style="min-width: 120px">备注:</div>
<div class="main">{{ parkingReviewInfo.parking.remark }}</div> <div class="main">{{ parkingReviewInfo.parking.remark }}</div>
</div> </div>
</div> </div>
</el-card> </el-card>
</div> </div>
<!-- 车辆信息 --> <!-- 车辆信息 -->
<div class="mb-10" v-if="!showCar"> <div v-if="!showCar" class="mb-10">
<el-card> <el-card>
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>车辆信息</span> <span>车辆信息</span>
</div> </div>
</template> </template>
<div class="w-full h-full CarInfoBox" v-if="parkingReviewInfo.car"> <div v-if="parkingReviewInfo.car" class="w-full h-full CarInfoBox">
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<div class="carInfo_item"> <div class="carInfo_item">
@@ -126,10 +126,10 @@
<el-image <el-image
v-for="(item, index) in parkingReviewInfo.car.carImageUrlList" v-for="(item, index) in parkingReviewInfo.car.carImageUrlList"
:key="index" :key="index"
class="carInfoImage"
:src="item"
:initial-index="index" :initial-index="index"
:preview-src-list="parkingReviewInfo.car.carImageUrlList" :preview-src-list="parkingReviewInfo.car.carImageUrlList"
:src="item"
class="carInfoImage"
preview-teleported preview-teleported
></el-image> ></el-image>
</div> </div>
@@ -137,7 +137,7 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<div class="w-full h-full CarInfoBox flex flex-center min-h-17rem" v-else> <div v-else class="w-full h-full CarInfoBox flex flex-center min-h-17rem">
<span style="color: #999; font-weight: 500; font-size: larger; letter-spacing: 10px">暂无信息</span> <span style="color: #999; font-weight: 500; font-size: larger; letter-spacing: 10px">暂无信息</span>
</div> </div>
</el-card> </el-card>
@@ -152,7 +152,7 @@
<div class="w-full h-full flex mt-2"> <div class="w-full h-full flex mt-2">
<div class="title">审批意见</div> <div class="title">审批意见</div>
<div class="preivewBodybox flex-1 ml-5"> <div class="preivewBodybox flex-1 ml-5">
<el-input maxlength="200" show-word-limit v-model="form.propertyReviewFeedback" type="textarea" :rows="5"></el-input> <el-input v-model="form.propertyReviewFeedback" :rows="5" maxlength="200" show-word-limit type="textarea"></el-input>
<div class="actions mt-5"> <div class="actions mt-5">
<el-button :disabled="parkingReviewInfo.parking.checkStatus === '1'" type="primary" @click="handlePreview(1)">审核通过</el-button> <el-button :disabled="parkingReviewInfo.parking.checkStatus === '1'" type="primary" @click="handlePreview(1)">审核通过</el-button>
<el-button :disabled="parkingReviewInfo.parking.checkStatus === '1'" type="danger" @click="handlePreview(2)" <el-button :disabled="parkingReviewInfo.parking.checkStatus === '1'" type="danger" @click="handlePreview(2)"
@@ -166,7 +166,7 @@
</div> </div>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-card style="position: relative" class="h-full"> <el-card class="h-full" style="position: relative">
<template #header> <template #header>
<div class="asideCardHeader w-full h-full"> <div class="asideCardHeader w-full h-full">
<div class="cardSubtitle"> <div class="cardSubtitle">
@@ -175,34 +175,12 @@
</div> </div>
</div> </div>
</template> </template>
<div class="w-full h-50%"> <div class="w-full h-full">
<el-steps space="200px" direction="vertical" :active="activeStop"> <StepTimeline :steps="stepList">
<el-step :description="active <= 0 ? '' : parkingReviewInfo.parkingReviewProcess.submitTime"> <template #title="{ item }">{{ item.title }}</template>
<template #title> <template #subtitle="{ item }">{{ item.subtitle }}</template>
<span v-if="active === 0">未提交信息</span> <template #time="{ item }">{{ item.time }}</template>
<span v-if="active >= 1">已提交信息</span> </StepTimeline>
</template>
</el-step>
<el-step :description="active <= 2 ? '' : parkingReviewInfo.parkingReviewProcess.propertyTime">
<template #title>
<span v-if="active <= 2">待审核</span>
<span v-if="active === 3">物业审核不通过</span>
<span v-if="active >= 4">物业审核通过</span>
</template>
</el-step>
<el-step :description="active <= 5 ? '' : parkingReviewInfo.parkingReviewProcess.certificationTime">
<template #title>
<span v-if="active <= 5">未认证</span>
<span v-if="active === 6">已认证</span>
</template>
</el-step>
<el-step>
<template #title>
<span v-if="active <= 5">审核未完成</span>
<span v-else>审核完成</span>
</template>
</el-step>
</el-steps>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
@@ -210,11 +188,12 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { getParkingReview, getParkingReviewInfo } from '@/api/system/parking'; import { getParkingReview, getParkingReviewInfo } from '@/api/system/parking';
import DictTag from '@/components/DictTag/index.vue'; import DictTag from '@/components/DictTag/index.vue';
import StepTimeline, { StepItem } from '@/components/StepTimeline/index.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_parking_status } = toRefs<any>(proxy?.useDict('com_parking_status')); const { com_parking_status } = toRefs<any>(proxy?.useDict('com_parking_status'));
@@ -271,7 +250,7 @@ type parkingReviewType = {
'carImageUrl': string; 'carImageUrl': string;
'userName': null; 'userName': null;
'userId': string; 'userId': string;
carImageUrlList?: string[]; 'carImageUrlList'?: string[];
}; };
parkingReviewProcess: parkingReviewProcess; parkingReviewProcess: parkingReviewProcess;
}; };
@@ -305,6 +284,10 @@ type parkingReviewProcess = {
submitTime: string; submitTime: string;
propertyTime: string; propertyTime: string;
certificationTime: string; certificationTime: string;
'parkingId': string;
'propertyReviewFeedback': string;
'carId': null;
'residentId': null;
}; };
const parkingReviewInfo = ref<parkingReviewType>({ const parkingReviewInfo = ref<parkingReviewType>({
parking: { parking: {
@@ -350,7 +333,11 @@ const parkingReviewInfo = ref<parkingReviewType>({
certificationStatus: null, certificationStatus: null,
submitTime: null, submitTime: null,
propertyTime: null, propertyTime: null,
certificationTime: null certificationTime: null,
parkingId: null,
propertyReviewFeedback: null,
carId: null,
residentId: null
} }
}); });
@@ -367,12 +354,19 @@ function getInfo() {
res.data.car.carImageUrlList = res.data.car.carImageUrl.split(',').filter(Boolean) ?? []; res.data.car.carImageUrlList = res.data.car.carImageUrl.split(',').filter(Boolean) ?? [];
} }
parkingReviewInfo.value = res.data; parkingReviewInfo.value = res.data;
form.value.propertyReviewFeedback = res.data.parkingReviewProcess.propertyReviewFeedback || '';
handleActiveStaus(res.data.parkingReviewProcess); handleActiveStaus(res.data.parkingReviewProcess);
}); });
} }
} }
getInfo(); getInfo();
const stepList = ref<StepItem[]>([
{ status: 'completed', title: '提交信息', subtitle: '', time: '', wrap: true },
{ status: 'pending', title: '物业审核', subtitle: '', time: '', wrap: true },
{ status: 'waiting', title: '认证通过', subtitle: '', time: '', wrap: true }
]);
// 处理审核进度 // 处理审核进度
function handleActiveStaus(rows: parkingReviewProcess) { function handleActiveStaus(rows: parkingReviewProcess) {
if (!rows) { if (!rows) {
@@ -389,6 +383,11 @@ function handleActiveStaus(rows: parkingReviewProcess) {
active.value = 1; // 提交信息 active.value = 1; // 提交信息
activeStop.value = 1; activeStop.value = 1;
stepList.value[0].status = 'completed';
stepList.value[0].subtitle = '';
stepList.value[0].time = rows.submitTime;
stepList.value[1].status = 'pending';
// 审核 // 审核
if (rows.propertyStatus === ReviewStatus.awaitReview) { if (rows.propertyStatus === ReviewStatus.awaitReview) {
active.value = 2; // 待审核 active.value = 2; // 待审核
@@ -398,6 +397,10 @@ function handleActiveStaus(rows: parkingReviewProcess) {
active.value = 4; // 审核通过 active.value = 4; // 审核通过
activeStop.value = 2; activeStop.value = 2;
stepList.value[1].time = rows.propertyTime;
stepList.value[1].status = 'completed';
stepList.value[2].status = 'pending';
// 认证 // 认证
if (rows.certificationStatus === certificationStatus.uncertification) { if (rows.certificationStatus === certificationStatus.uncertification) {
active.value = 5; // 未认证 active.value = 5; // 未认证
@@ -406,6 +409,10 @@ function handleActiveStaus(rows: parkingReviewProcess) {
} else if (rows.certificationStatus === certificationStatus.Certification) { } else if (rows.certificationStatus === certificationStatus.Certification) {
active.value = 6; // 已认证 active.value = 6; // 已认证
activeStop.value = 4; activeStop.value = 4;
stepList.value[2].time = rows.certificationTime;
stepList.value[2].status = 'completed';
stepList.value[3].status = 'completed';
} }
} }
} }
@@ -430,7 +437,7 @@ function handleBack() {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.ResidentMainBox { .ResidentMainBox {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="p-2 h-full flex flex-col"> <div class="p-2 h-full flex flex-col">
<pageHeader></pageHeader> <pageHeader></pageHeader>
<div class="overflow-auto flex-1">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :inline="true" :model="queryParams" :rules="formRules" label-width="140" @submit.prevent="handleQuery"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" :rules="formRules" label-width="140" @submit.prevent="handleQuery">
<div> <div>
@@ -143,17 +144,12 @@
</div> </div>
</el-form> </el-form>
</el-card> </el-card>
<el-card class="flex-1" shadow="never" style="margin-top: 5px"> <el-card shadow="never" style="margin-top: 5px; height: 800px">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="5">
<span>租赁管理明细</span> <span>租赁管理明细</span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" border> <el-table v-loading="loading" :data="ArrearsDetailList" border>
<el-table-column align="center" label="区域" prop="area" /> <el-table-column align="center" label="区域" prop="area" />
@@ -196,6 +192,7 @@
<pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</div>
</template> </template>
<script lang="ts" name="Arrears" setup> <script lang="ts" name="Arrears" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';

View File

@@ -4,19 +4,19 @@
<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" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" @submit.prevent="handleQuery">
<div class="flex justify-between"> <div class="flex justify-between">
<div> <div>
<el-form-item label-width="100" label="截至应收账期" prop="villageId"> <el-form-item label="截至应收账期" label-width="100" prop="villageId">
<el-date-picker <el-date-picker
v-model="queryTime" v-model="queryTime"
type="daterange"
@update:model-value="handleQueryTime"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY-MM" format="YYYY-MM"
range-separator="至"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM" value-format="YYYY-MM"
@update:model-value="handleQueryTime"
/> />
</el-form-item> </el-form-item>
<el-form-item label="小区" prop="villageId"> <el-form-item label="小区" prop="villageId">
@@ -30,12 +30,12 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="账龄区间" prop="agingRange"> <el-form-item label="账龄区间" prop="agingRange">
<el-select :multiple="true" v-model="queryParams.agingRange" placeholder="请选择"> <el-select v-model="queryParams.agingRange" :multiple="true" placeholder="请选择">
<el-option v-for="item in [...com_report_age_list]" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in [...com_report_age_list]" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>
<div> <div style="width: 180px">
<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>
</div> </div>
@@ -45,37 +45,32 @@
</div> </div>
</transition> </transition>
<el-card shadow="never" class="flex-1"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="5">
<span>账龄分析汇总表</span> <span>账龄分析汇总表</span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table :span-method="arraySpanMethod" v-loading="loading" border :data="AgingList"> <el-table v-loading="loading" :data="AgingList" :span-method="arraySpanMethod" border>
<el-table-column label="截至应收账期" align="center" prop="cutoffPeriod" /> <el-table-column align="center" label="截至应收账期" prop="cutoffPeriod" />
<el-table-column label="小区项目" align="center" prop="villageName" /> <el-table-column align="center" label="小区项目" prop="villageName" />
<el-table-column label="房屋类型" align="center" prop="houseTypeName" /> <el-table-column align="center" label="房屋类型" prop="houseTypeName" />
<el-table-column label="≤6个月" align="center" prop="amountLe6" /> <el-table-column align="center" label="≤6个月" prop="amountLe6" />
<el-table-column label="6个月-12个月" align="center" prop="amountM6To12" /> <el-table-column align="center" label="6个月-12个月" prop="amountM6To12" />
<el-table-column label="1年-2年" align="center" prop="amountY1To2" /> <el-table-column align="center" label="1年-2年" prop="amountY1To2" />
<el-table-column label="2年-3年" align="center" prop="amountY2To3" /> <el-table-column align="center" label="2年-3年" prop="amountY2To3" />
<el-table-column label="3年-4年" align="center" prop="amountY3To4" /> <el-table-column align="center" label="3年-4年" prop="amountY3To4" />
<el-table-column label="4年-5年" align="center" prop="amountY4To5" /> <el-table-column align="center" label="4年-5年" prop="amountY4To5" />
<el-table-column label="5年以上" align="center" prop="amountOver5" /> <el-table-column align="center" label="5年以上" prop="amountOver5" />
<el-table-column label="合计" align="center" prop="totalAmount" /> <el-table-column align="center" label="合计" prop="totalAmount" />
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="Arrears" lang="ts"> <script lang="ts" name="Arrears" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import { listHouseUseType } from '@/api/system/houseUse'; import { listHouseUseType } from '@/api/system/houseUse';

View File

@@ -4,11 +4,11 @@
<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" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" @submit.prevent="handleQuery">
<div class="flex justify-between"> <div class="flex justify-between">
<div> <div>
<el-form-item label="小区" prop="villageId"> <el-form-item label="小区" prop="villageId">
<el-select @change="handleChangeVillage" v-model="queryParams.villageId" placeholder="请选择"> <el-select v-model="queryParams.villageId" placeholder="请选择" @change="handleChangeVillage">
<el-option v-for="item in villageList" :key="item.villageId" :label="item.villageName" :value="item.villageId" /> <el-option v-for="item in villageList" :key="item.villageId" :label="item.villageName" :value="item.villageId" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -26,12 +26,12 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="账龄区间" prop="agingRange"> <el-form-item label="账龄区间" prop="agingRange">
<el-select :multiple="true" v-model="queryParams.agingRange" placeholder="请选择"> <el-select v-model="queryParams.agingRange" :multiple="true" placeholder="请选择">
<el-option v-for="item in [...com_report_age_list]" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in [...com_report_age_list]" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>
<div> <div style="width: 200px">
<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>
</div> </div>
@@ -41,40 +41,35 @@
</div> </div>
</transition> </transition>
<el-card shadow="never" class="flex-1"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="5">
<span>账龄分析明细表</span> <span>账龄分析明细表</span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table :span-method="arraySpanMethod" v-loading="loading" border :data="AgingList"> <el-table v-loading="loading" :data="AgingList" :span-method="arraySpanMethod" border>
<el-table-column label="截至应收账期" align="center" prop="cutoffPeriod" /> <el-table-column align="center" label="截至应收账期" prop="cutoffPeriod" />
<el-table-column label="小区项目" align="center" prop="villageName" /> <el-table-column align="center" label="小区项目" prop="villageName" />
<el-table-column label="楼栋" align="center" prop="buildingName" /> <el-table-column align="center" label="楼栋" prop="buildingName" />
<el-table-column label="客户名称" align="center" prop="customerName" /> <el-table-column align="center" label="客户名称" prop="customerName" />
<el-table-column label="房号编号" align="center" prop="roomNo" /> <el-table-column align="center" label="房号编号" prop="roomNo" />
<el-table-column label="欠费月数" align="center" prop="arrearsMonthsLabel" /> <el-table-column align="center" label="欠费月数" prop="arrearsMonthsLabel" />
<el-table-column label="≤6个月" align="center" prop="amountLe6" /> <el-table-column align="center" label="≤6个月" prop="amountLe6" />
<el-table-column label="6个月-12个月" align="center" prop="amountM6To12" /> <el-table-column align="center" label="6个月-12个月" prop="amountM6To12" />
<el-table-column label="1年-2年" align="center" prop="amountY1To2" /> <el-table-column align="center" label="1年-2年" prop="amountY1To2" />
<el-table-column label="2年-3年" align="center" prop="amountY2To3" /> <el-table-column align="center" label="2年-3年" prop="amountY2To3" />
<el-table-column label="3年-4年" align="center" prop="amountY3To4" /> <el-table-column align="center" label="3年-4年" prop="amountY3To4" />
<el-table-column label="4年-5年" align="center" prop="amountY4To5" /> <el-table-column align="center" label="4年-5年" prop="amountY4To5" />
<el-table-column label="5年以上" align="center" prop="amountOver5" /> <el-table-column align="center" label="5年以上" prop="amountOver5" />
<el-table-column label="合计" align="center" prop="totalAmount" /> <el-table-column align="center" label="合计" prop="totalAmount" />
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="Arrears" lang="ts"> <script lang="ts" name="Arrears" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import { getCommunitylistAPI } from '@/api/system/community'; import { getCommunitylistAPI } from '@/api/system/community';
@@ -222,7 +217,7 @@ const handleExport = () => {
{ label: '截至应收账期', prop: 'cutoffPeriod' }, { label: '截至应收账期', prop: 'cutoffPeriod' },
{ label: '小区项目', prop: 'villageName' }, { label: '小区项目', prop: 'villageName' },
{ label: '楼栋', prop: 'buildingName' }, { label: '楼栋', prop: 'buildingName' },
{ label: '客户名称', prop: 'buildingName' }, { label: '客户名称', prop: 'customerName' },
{ label: '房号编号', prop: 'roomNo' }, { label: '房号编号', prop: 'roomNo' },
{ label: '欠费月数', prop: 'arrearsMonthsLabel' }, { label: '欠费月数', prop: 'arrearsMonthsLabel' },
{ label: '≤6个月', prop: 'amountLe6' }, { label: '≤6个月', prop: 'amountLe6' },

View File

@@ -35,21 +35,18 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" style="margin-bottom: 30px"> <div class="flex justify-between items-center" style="height: 40px; margin-bottom: 60px">
<el-col :span="5">
<div class="flex items-center"> <div class="flex items-center">
<div style="width: 200px; font-size: 15px; margin-right: 10px">应收欠费汇总列表</div> <div style="width: 125px; font-size: 15px; margin-right: 10px">应收欠费汇总列表</div>
<el-select v-model="queryParams.viewType" placeholder="请选择视图类型" @change="handleViewTypeChange"> <el-select v-model="queryParams.viewType" placeholder="请选择视图类型" style="width: 200px" @change="handleViewTypeChange">
<el-option :value="1" label="维度1(按收费类型)" /> <el-option :value="1" label="维度1(按收费类型)" />
<el-option :value="2" label="维度2(按房屋类型)" /> <el-option :value="2" label="维度2(按房屋类型)" />
</el-select> </el-select>
</div> </div>
</el-col> <div>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row> </div>
</template> </template>
<el-table <el-table

View File

@@ -51,7 +51,7 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="chargeTrendList" border> <el-table v-loading="loading" :data="chargeTrendList" border style="min-height: 80px">
<el-table-column v-for="(item, index) in rows" :key="index" :label="item" align="center"> <el-table-column v-for="(item, index) in rows" :key="index" :label="item" align="center">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row[item] }}</span> <span>{{ scope.row[item] }}</span>

View File

@@ -25,21 +25,16 @@
</transition> </transition>
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="4">
<el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleTabClick"> <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleTabClick">
<el-tab-pane label="按小区统计" name="first"> </el-tab-pane> <el-tab-pane label="按小区统计" name="first"> </el-tab-pane>
<el-tab-pane label="按收费项目统计" name="fourth"></el-tab-pane> <el-tab-pane label="按收费项目统计" name="fourth"></el-tab-pane>
</el-tabs> </el-tabs>
</el-col>
<el-col :span="19"></el-col>
<el-col :span="1">
<el-button type="primary" @click="exportData">导出</el-button> <el-button type="primary" @click="exportData">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table v-loading="loading" :data="AgingList" border> <el-table v-loading="loading" :data="AgingList" border>
<el-table-column v-if="activeName === 'first'" align="center" label="小区名称" prop="villageName" /> <el-table-column v-if="activeName === 'first'" align="center" label="项目" prop="villageName" />
<el-table-column v-else align="center" label="收费项目" prop="chargeItemName" /> <el-table-column v-else align="center" label="收费项目" prop="chargeItemName" />
<el-table-column v-for="(item, index) in rows" :key="index" :label="item.payTypeName" align="center"> <el-table-column v-for="(item, index) in rows" :key="index" :label="item.payTypeName" align="center">
<template #default="scope"> <template #default="scope">

View File

@@ -52,8 +52,13 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="chargeTrendList" border> <el-table v-loading="loading" :data="chargeTrendList" border style="min-height: 80px">
<el-table-column v-for="(item, index) in rows" :key="index" :label="item" align="center"> <el-table-column align="center" fixed="left" label="月份">
<template #default>
<span>金额</span>
</template>
</el-table-column>
<el-table-column v-for="(item, index) in rows" :key="index" :label="item" align="center" min-width="100">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row[item] }}</span> <span>{{ scope.row[item] }}</span>
</template> </template>
@@ -66,7 +71,6 @@
</el-card> </el-card>
</div> </div>
</template> </template>
账龄
<script lang="ts" name="Arrears" setup> <script lang="ts" name="Arrears" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
@@ -85,8 +89,13 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const queryTime = ref([]); const queryTime = ref([]);
function handleQueryTime(val: string[]) { function handleQueryTime(val: string[]) {
if (val && val.length > 0) {
queryParams.value.startTime = val[0]; queryParams.value.startTime = val[0];
queryParams.value.endTime = val[1]; queryParams.value.endTime = val[1];
} else {
queryParams.value.startTime = '';
queryParams.value.endTime = '';
}
} }
const data = reactive({ const data = reactive({
queryParams: { queryParams: {
@@ -145,9 +154,11 @@ const getList = async () => {
getChargeTend(search) getChargeTend(search)
.then((res) => { .then((res) => {
const obj = {}; const obj = {};
const length = res.data.length;
res.data.forEach((item) => { res.data.forEach((item) => {
const label = item.month.split('-')[1]; const label = item.month.split('-')[1];
const key = `${label}月`; // const key = `${label}月`;
const key = length > 12 ? `${item.month}月` : `${label}月`;
rows.value.push(key); rows.value.push(key);
obj[key] = item.amount; obj[key] = item.amount;
}); });
@@ -219,6 +230,7 @@ const chartConfig = shallowRef<EChartsOption>({
function handleChartsData() { function handleChartsData() {
const data = Object.entries(chargeTrendList.value[0]); const data = Object.entries(chargeTrendList.value[0]);
console.log(data);
const x = data.map((item) => item[0]); const x = data.map((item) => item[0]);
const y = data.map((item) => item[1]); const y = data.map((item) => item[1]);
// 如果使用的是 ref,直接修改属性即可触发响应式 // 如果使用的是 ref,直接修改属性即可触发响应式

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="p-2 h-full flex flex-col"> <div class="p-2 h-full flex flex-col">
<pageHeader></pageHeader> <pageHeader></pageHeader>
<div class="flex-1 overflow-auto">
<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">
@@ -10,7 +11,7 @@
:model="queryParams" :model="queryParams"
:rules="formRules" :rules="formRules"
label-position="right" label-position="right"
label-width="180px" label-width="170px"
@submit.prevent="handleQuery" @submit.prevent="handleQuery"
> >
<div> <div>
@@ -167,18 +168,15 @@
</el-card> </el-card>
</div> </div>
</transition> </transition>
<el-card class="el-table-box" shadow="never">
<el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10"> <div class="flex items-center justify-between">
<el-col :span="5"> <span>收费日报</span>
<span> 收费日报 </span> <div>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> <el-button type="primary" @click="handleExportAll">导出所有</el-button>
</el-row> </div>
</div>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" :row-class-name="handleRowClass" border> <el-table v-loading="loading" :data="ArrearsDetailList" :row-class-name="handleRowClass" border>
<el-table-column align="center" fixed="left" label="日期" prop="payTime" width="100" /> <el-table-column align="center" fixed="left" label="日期" prop="payTime" width="100" />
@@ -242,6 +240,7 @@
<pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -428,7 +427,9 @@ const getList = async () => {
getDailyFeeReportList(search) getDailyFeeReportList(search)
.then((res) => { .then((res) => {
ArrearsDetailList.value = [...(res.rows ?? [])]; ArrearsDetailList.value = [...(res.rows ?? [])];
if (res.rows) {
rowChargeTypesList.value = Object.keys(res.rows[0].chargeTypeAmounts) ?? []; rowChargeTypesList.value = Object.keys(res.rows[0].chargeTypeAmounts) ?? [];
}
total.value = res.total; total.value = res.total;
}) })
.finally(() => { .finally(() => {
@@ -485,7 +486,30 @@ const handleExport = () => {
proxy?.$modal.msgWarning('没有可导出的数据'); proxy?.$modal.msgWarning('没有可导出的数据');
return; return;
} }
handleExportDate(ArrearsDetailList.value);
};
/** 导出所有Excel */
const handleExportAll = () => {
let data = [];
const search = {
...queryParams.value
};
// 移除空字符串字段
Object.keys(search).forEach((key) => {
if (search[key] === '-1') {
search[key] = undefined;
}
});
getDailyFeeReportList(search)
.then((res) => {
data = [...(res.rows ?? [])];
handleExportDate(data);
})
.finally(() => {
loading.value = false;
});
};
const handleExportDate = (data: Datum[], title: string = '收费日报') => {
// 显示加载提示 // 显示加载提示
loading.value = true; loading.value = true;
// 使用 setTimeout 让出主线程,避免阻塞UI // 使用 setTimeout 让出主线程,避免阻塞UI
@@ -550,7 +574,16 @@ const handleExport = () => {
// { label: '经办人', prop: 'inspectorName' }, // { label: '经办人', prop: 'inspectorName' },
// { label: '备注', prop: 'remark' } // { label: '备注', prop: 'remark' }
]; ];
exportToExcel(ArrearsDetailList.value, columns, `日报_${new Date().getTime()}`); exportToExcel(data, columns, `${title}_${new Date().getTime()}`, undefined, ({ row }) => {
if (row.residentName === '当日合计') {
return {
fill: {
fgColor: { rgb: 'E2EFDA' },
patternType: 'solid'
}
};
}
});
} catch (error) { } catch (error) {
proxy?.$modal.msgError('导出失败,请重试'); proxy?.$modal.msgError('导出失败,请重试');
console.error('导出错误:', error); console.error('导出错误:', error);
@@ -559,6 +592,7 @@ const handleExport = () => {
} }
}, 100); }, 100);
}; };
onMounted(() => { onMounted(() => {
getList(); getList();
getHouseUseList(); getHouseUseList();
@@ -568,6 +602,9 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.el-table-box {
height: 800px;
}
.el-table { .el-table {
height: calc(100% - 80px); height: calc(100% - 80px);
} }

View File

@@ -27,13 +27,13 @@
<el-form-item label="房号" prop="houseNo"> <el-form-item label="房号" prop="houseNo">
<el-input v-model.trim="queryParams.houseNo" placeholder="请输入房号" /> <el-input v-model.trim="queryParams.houseNo" placeholder="请输入房号" />
</el-form-item> </el-form-item>
<el-form-item label="欠费月数" prop="arrearsMonths"> <!-- <el-form-item label="欠费月数" prop="arrearsMonths">-->
<el-select v-model="queryParams.arrearsMonths" placeholder="请选择"> <!-- <el-select v-model="queryParams.arrearsMonths" placeholder="请选择">-->
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" /> <!-- <el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />-->
</el-select> <!-- </el-select>-->
</el-form-item> <!-- </el-form-item>-->
</div> </div>
<div> <div style="width: 180px">
<el-button :loading="loading" type="primary" @click="handleQuery">查询</el-button> <el-button :loading="loading" type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button> <el-button @click="resetQuery">重置</el-button>
</div> </div>
@@ -45,16 +45,13 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center" style="height: 40px; margin-bottom: 15px">
<el-col :span="5">
<span>应收欠费明细表</span> <span>应收欠费明细表</span>
</el-col> <div>
<el-col :span="15"></el-col>
<el-col :span="3">
<el-button type="primary" @click="handleExport">导出本页</el-button> <el-button type="primary" @click="handleExport">导出本页</el-button>
<el-button type="primary" @click="handleExportAll">导出全部</el-button> <el-button type="primary" @click="handleExportAll">导出全部</el-button>
</el-col> </div>
</el-row> </div>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" :span-method="mergeSpanMethod" border> <el-table v-loading="loading" :data="ArrearsDetailList" :span-method="mergeSpanMethod" border>
@@ -228,7 +225,8 @@ const handleExport = () => {
children: [ children: [
...dynamicColumns.value.map((item) => { ...dynamicColumns.value.map((item) => {
return { label: item, prop: `chargeItemAmounts.${item}` }; return { label: item, prop: `chargeItemAmounts.${item}` };
}) }),
{ label: '金额合计', prop: 'totalAmount' }
] ]
} }
]; ];

View File

@@ -4,11 +4,11 @@
<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="80px" :model="queryParams" :rules="rules" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" :inline="true" :model="queryParams" :rules="rules" label-width="80px" @submit.prevent="handleQuery">
<div class="flex justify-between"> <div class="flex justify-between">
<div> <div>
<el-form-item label="楼栋(多选)" prop="buildingIds"> <el-form-item label="楼栋(多选)" prop="buildingIds">
<el-select multiple v-model="queryParams.buildingIds" placeholder="请选择"> <el-select v-model="queryParams.buildingIds" multiple placeholder="请选择">
<el-option v-for="item in allHouseUseList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in allHouseUseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -39,7 +39,7 @@
</el-form-item> </el-form-item>
</div> </div>
</div> </div>
<div> <div style="width: 210px">
<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>
</div> </div>
@@ -49,46 +49,40 @@
</div> </div>
</transition> </transition>
<el-card shadow="never" class="flex-1"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" style="margin-bottom: 30px"> <div class="flex justify-between items-center">
<el-col :span="5">
<span> 收费月报 </span> <span> 收费月报 </span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" border>
<el-table v-loading="loading" border :data="ArrearsDetailList"> <el-table-column align="center" label="楼栋" prop="buildingName" />
<el-table-column label="楼栋" align="center" prop="buildingName" /> <el-table-column align="center" label="本月应收" prop="monthReceivable" />
<el-table-column label="本月应收" align="center" prop="monthReceivable" /> <el-table-column align="center" label="本月已收" prop="monthReceived" />
<el-table-column label="本月已收" align="center" prop="monthReceived" /> <el-table-column align="center" label="本月未收" prop="monthUnpaid" />
<el-table-column label="本月未收" align="center" prop="monthUnpaid" /> <el-table-column align="center" label="月收费率" prop="monthRate">
<el-table-column label="月收费率" align="center" prop="monthRate">
<template #default="scope"> <template #default="scope">
{{ `${scope.row.monthRate}%` || '0%' }} {{ `${scope.row.monthRate}%` || '0%' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="本年应收" align="center" prop="yearReceivable" /> <el-table-column align="center" label="本年应收" prop="yearReceivable" />
<el-table-column label="本年已收" align="center" prop="yearReceived" /> <el-table-column align="center" label="本年已收" prop="yearReceived" />
<el-table-column label="本年未收" align="center" prop="yearUnpaid" /> <el-table-column align="center" label="本年未收" prop="yearUnpaid" />
<el-table-column label="年收费率" align="center" prop="yearRate"> <el-table-column align="center" label="年收费率" prop="yearRate">
<template #default="scope"> <template #default="scope">
{{ `${scope.row.yearRate}%` || '0%' }} {{ `${scope.row.yearRate}%` || '0%' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="综合欠收" align="center" prop="totalPrepaid" /> <el-table-column align="center" label="综合欠收" prop="totalPrepaid" />
<el-table-column label="综合预收" align="center" prop="totalArrears" /> <el-table-column align="center" label="综合预收" prop="totalArrears" />
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import { ColumnConfig, exportToExcel } from '@/utils/xlsx'; import { ColumnConfig, exportToExcel } from '@/utils/xlsx';

View File

@@ -42,17 +42,11 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="5">
<span>实收物业费详情 </span> <span>实收物业费详情 </span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" border> <el-table v-loading="loading" :data="ArrearsDetailList" border>
<el-table-column align="center" label="收费所属期" prop="name" /> <el-table-column align="center" label="收费所属期" prop="name" />
<el-table-column align="center" label="序号" prop="sequence" /> <el-table-column align="center" label="序号" prop="sequence" />
@@ -70,7 +64,6 @@
</el-card> </el-card>
</div> </div>
</template> </template>
账龄
<script lang="ts" setup> <script lang="ts" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';

View File

@@ -39,20 +39,13 @@
</el-card> </el-card>
</div> </div>
</transition> </transition>
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="5">
<span>应收物业费详情 </span> <span>应收物业费详情 </span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" border> <el-table v-loading="loading" :data="ArrearsDetailList" border>
<el-table-column align="center" label="收费所属期" prop="name" /> <el-table-column align="center" label="收费所属期" prop="name" />
<el-table-column align="center" label="序号" prop="sequence" /> <el-table-column align="center" label="序号" prop="sequence" />
@@ -70,7 +63,6 @@
</el-card> </el-card>
</div> </div>
</template> </template>
账龄
<script lang="ts" setup> <script lang="ts" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';

View File

@@ -35,15 +35,10 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="5">
<span> 预收费用余额汇总 </span> <span> 预收费用余额汇总 </span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" border> <el-table v-loading="loading" :data="ArrearsDetailList" border>
@@ -64,7 +59,6 @@
</el-card> </el-card>
</div> </div>
</template> </template>
账龄
<script lang="ts" setup> <script lang="ts" setup>
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';

View File

@@ -33,15 +33,10 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <div class="flex justify-between items-center">
<el-col :span="5"> <span> 预收费用余额明细 </span>
<span> 预收费用余额详细 </span>
</el-col>
<el-col :span="18"></el-col>
<el-col :span="1">
<el-button type="primary" @click="handleExport">导出</el-button> <el-button type="primary" @click="handleExport">导出</el-button>
</el-col> </div>
</el-row>
</template> </template>
<el-table v-loading="loading" :data="ArrearsDetailList" border> <el-table v-loading="loading" :data="ArrearsDetailList" border>
@@ -50,7 +45,7 @@
<el-table-column align="center" label="户号" prop="houseNo" /> <el-table-column align="center" label="户号" prop="houseNo" />
<el-table-column align="center" label="客户" prop="customerName" /> <el-table-column align="center" label="客户" prop="customerName" />
<el-table-column align="center" label="预收账期" prop="prepaidPeriod" /> <el-table-column align="center" label="预收账期" prop="prepaidPeriod" />
<el-table-column align="center" label="预收月份" prop="prepaidMonths" /> <el-table-column align="center" label="预收月数" prop="prepaidMonths" />
<el-table-column align="center" label="收费项目" prop="chargeItemAmounts"> <el-table-column align="center" label="收费项目" prop="chargeItemAmounts">
<template #default> <template #default>
<el-table-column v-for="item in rowChargeTypesList" :key="item" :label="item.chargeItemName" align="center"> <el-table-column v-for="item in rowChargeTypesList" :key="item" :label="item.chargeItemName" align="center">

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