修复小区背景颜色,巡检点顺序一致,投诉处理按钮

This commit is contained in:
Zy
2026-07-16 11:06:26 +08:00
parent 4efb535730
commit 725bdfa3fe
56 changed files with 1034 additions and 468 deletions

View File

@@ -9,9 +9,9 @@ VITE_APP_ENV='development'
# 开发环境 # 开发环境
VITE_APP_BASE_API='/dev-api' VITE_APP_BASE_API='/dev-api'
# # 开发环境 接口代理地址 # # 开发环境 接口代理地址
VITE_APP_PROXY_API='http://192.168.1.222:8080' #VITE_APP_PROXY_API='http://192.168.1.222:8080'
# 开发环境 接口代理地址 # 开发环境 接口代理地址
#VITE_APP_PROXY_API='http://192.168.1.6:8080' VITE_APP_PROXY_API='http://192.168.1.6:8080'
# 图片基础地址 # 图片基础地址
VITE_IMG_URL='http://192.168.1.222:8080' VITE_IMG_URL='http://192.168.1.222:8080'

View File

@@ -80,3 +80,49 @@ export const list_statapi = (query?: { taskTimeBegin: string; taskTimeEnd: strin
params: query params: query
}); });
}; };
export type exportPageApiQuery = {
/**
* 数据类型1-日报 / 2-周报 / 3-月报
*/
dataType?: string;
/**
* 页码
*/
pageNum: number;
/**
* 每页条数
*/
pageSize: number;
/**
* 查询开始日期
*/
queryDateBegin?: string;
/**
* 查询结束日期
*/
queryDateEnd?: string;
/**
* 日期
*/
statDate?: string;
};
type exportAllApiQuery = Omit<exportPageApiQuery, 'pageSize' | 'pageNum'>;
/** 导出当前页 数据api */
export const exportPageApi = (data: exportPageApiQuery): AxiosPromise<any[]> => {
return request({
url: '/inspection/data/export/page',
method: 'POST',
data
});
};
/** 导出 所有 数据api */
export const exportAllApi = (data: exportAllApiQuery): AxiosPromise<any[]> => {
return request({
url: '/inspection/data/export',
method: 'POST',
data
});
};

View File

@@ -3,17 +3,12 @@ export interface PlanVO {
* 主键ID * 主键ID
*/ */
id: string | number; id: string | number;
inspectorIds: string;
/** /**
* 巡检计划名称 * 巡检计划名称
*/ */
planName: string; planName: string;
/**
* 巡检人员ID多个用逗号分隔
*/
inspectorIds: string;
/** /**
* 执行周期,如:开始日期-结束日期 * 执行周期,如:开始日期-结束日期
*/ */
@@ -33,6 +28,11 @@ export interface PlanVO {
* 巡检设置0-必须拍照1-可以跳检 * 巡检设置0-必须拍照1-可以跳检
*/ */
inspectSetting: number; inspectSetting: number;
inspectSettingDict: {
dictLabel: string;
dictValue: string;
listClass: string;
};
/** /**
* 计划路线,多个用逗号分隔 * 计划路线,多个用逗号分隔
@@ -53,7 +53,11 @@ export interface PlanVO {
* 执行周期具体日期 * 执行周期具体日期
*/ */
executeDay: string; executeDay: string;
executeDayList: {
dictLabel: string;
dictValue: string;
listClass: string;
}[];
/** 巡检人员信息 */ /** 巡检人员信息 */
inspectorList: { inspectorList: {
'userId': string; 'userId': string;

View File

@@ -120,3 +120,13 @@ export const queryResidentList_Cars = (query: { pageNum: number; pageSize: numbe
params: query params: query
}); });
}; };
/**
* 生成账单
*/
export const manualGenerateApi = (data: { billId: string; chargePeriod: string; yearMonths?: string[]; year?: string[] }) => {
return request({
url: '/bill/manualGenerate',
method: 'POST',
data
});
};

View File

@@ -3,6 +3,7 @@ export interface BillVO {
* 账单管理表id * 账单管理表id
*/ */
id: string; id: string;
billName:string;
/** /**
* 账单编号 * 账单编号
@@ -106,7 +107,7 @@ export interface BillForm extends BaseEntity {
/** /**
* 创建时间 * 创建时间
*/ */
cerateTime?: string; createTime?: string;
} }
export interface BillQuery extends PageQuery { export interface BillQuery extends PageQuery {

View File

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

View File

@@ -80,7 +80,7 @@ export interface ResidentForm extends BaseEntity {
* 主键 住户管理id * 主键 住户管理id
*/ */
userId?: string; userId?: string;
unitNoId: string; unitNoId?: string;
/** /**
* 住户编号 * 住户编号

View File

@@ -260,30 +260,3 @@ aside {
border-radius: 5px; border-radius: 5px;
} }
.bgColor {
width: 850px;
height: 850px;
background: #fdf1ee;
filter: blur(200px);
display: block;
position: absolute;
top: 0;
right: 0;
transform: translateY(-50%) translateX(0%);
pointer-events: none;
z-index: 1;
border-radius: 50%;
@media (max-width: 1540px) {
width: 800px;
height: 400px;
}
@media (max-width: 1280px) {
width: 600px;
height: 300px;
}
@media (max-width: 1030px) {
width: 400px;
height: 200px;
}
}

View File

@@ -134,6 +134,31 @@ function clearLocation() {
markers.value = []; markers.value = [];
} }
let lastFitTime = 0;
let fitTimer: ReturnType<typeof setTimeout> | null = null;
const FIT_THROTTLE_MS = 100; // 节流间隔ms
// === 新增函数 ===
function throttledFitView() {
const now = Date.now();
if (now - lastFitTime >= FIT_THROTTLE_MS) {
// 距离上次调用已超过阈值,立即执行
if (fitTimer) {
clearTimeout(fitTimer);
fitTimer = null;
}
fitMapView(Array.from(AddMarkered.values()));
lastFitTime = now;
} else if (!fitTimer) {
// 未超过阈值,延迟到阈值时再执行
fitTimer = setTimeout(() => {
fitMapView(Array.from(AddMarkered.values()));
lastFitTime = Date.now();
fitTimer = null;
}, FIT_THROTTLE_MS);
}
// 如果已经有定时器在等,不重复创建
}
// ===================== 多点标记(可增删) ===================== // ===================== 多点标记(可增删) =====================
function addMarker_more(angular: number[], pointName: string, pointId: string) { function addMarker_more(angular: number[], pointName: string, pointId: string) {
if (!isMapAvailable()) { if (!isMapAvailable()) {
@@ -145,7 +170,8 @@ function addMarker_more(angular: number[], pointName: string, pointId: string) {
map.value.remove(AddMarkered.get(pointId)); map.value.remove(AddMarkered.get(pointId));
AddMarkered.delete(pointId); AddMarkered.delete(pointId);
// 删除后重新适配视野 // 删除后重新适配视野
fitMapView(Array.from(AddMarkered.values())); // fitMapView(Array.from(AddMarkered.values()));
throttledFitView();
return; return;
} }
@@ -161,7 +187,8 @@ function addMarker_more(angular: number[], pointName: string, pointId: string) {
}); });
AddMarkered.set(pointId, marker); AddMarkered.set(pointId, marker);
markers.value.push(marker); markers.value.push(marker);
fitMapView(Array.from(AddMarkered.values())); // fitMapView(Array.from(AddMarkered.values()));
throttledFitView();
return marker; return marker;
} }
@@ -320,6 +347,10 @@ onUnmounted(() => {
AMAP.value = null; AMAP.value = null;
map.value = null; map.value = null;
mapReady.value = false; mapReady.value = false;
if (fitTimer) {
clearTimeout(fitTimer);
fitTimer = null;
}
}); });
</script> </script>

View File

@@ -92,6 +92,28 @@ function loadTDT(): Promise<any> {
document.body.appendChild(script); document.body.appendChild(script);
}); });
} }
let lastFitTime = 0;
let fitTimer: ReturnType<typeof setTimeout> | null = null;
const FIT_THROTTLE_MS = 100; // 节流间隔ms
function throttledFitView(data: any[]) {
const now = Date.now();
if (now - lastFitTime >= FIT_THROTTLE_MS) {
if (fitTimer) {
clearTimeout(fitTimer);
fitTimer = null;
}
const points = data.map((m: any) => m.getLngLat());
fitMapView(points);
lastFitTime = now;
} else if (!fitTimer) {
fitTimer = setTimeout(() => {
const points = data.map((m: any) => m.getLngLat());
fitMapView(points);
lastFitTime = Date.now();
fitTimer = null;
}, FIT_THROTTLE_MS);
}
}
/** 根据提供的 坐标点数组 设置地图视野,调整后的视野会保证包含提供的坐标点。 */ /** 根据提供的 坐标点数组 设置地图视野,调整后的视野会保证包含提供的坐标点。 */
function fitMapView(overlays: any[] = []) { function fitMapView(overlays: any[] = []) {
@@ -106,7 +128,7 @@ function isMapAvailable() {
/** 点击事件 */ /** 点击事件 */
function bindMapClick() { function bindMapClick() {
if (!isMapAvailable()) return; if (!isMapAvailable()) return;
map.addEventListener('click', (e) => { map.addEventListener('click', (e: { lnglat: { lng: any; lat: any } }) => {
const { lng, lat } = e.lnglat; const { lng, lat } = e.lnglat;
emits('clickPoint', [lng, lat]); emits('clickPoint', [lng, lat]);
}); });
@@ -125,7 +147,7 @@ function addMarker_only(LngLat: LngLat, pointName: string = '巡检点') {
//向地图上添加标注 //向地图上添加标注
markers = [marker]; markers = [marker];
map.addOverLay(marker); map.addOverLay(marker);
fitMapView(markers.map((item) => item.getLngLat())); throttledFitView(markers);
return marker; return marker;
} }
/** 清空单点标记 */ /** 清空单点标记 */
@@ -151,6 +173,7 @@ function addMarker_more(LngLat: LngLat, pointName: string, pointId: string) {
}); });
PointMap.set(pointId, marker); PointMap.set(pointId, marker);
map.addOverLay(marker); map.addOverLay(marker);
throttledFitView([marker]);
} }
// !=================================================================== // !===================================================================
@@ -164,7 +187,7 @@ function initDrawTool() {
handler.on('draw', lineDraw); handler.on('draw', lineDraw);
} }
// 监听 绘画完成事件 // 监听 绘画完成事件
function lineDraw(data) { function lineDraw(data: { currentPolyline: any }) {
if (lines.length > 0) { if (lines.length > 0) {
lines.forEach((item) => { lines.forEach((item) => {
map.removeOverLay(item); map.removeOverLay(item);
@@ -225,6 +248,10 @@ function clearMapMarker() {
} else { } else {
waitQueue.value.clear = true; waitQueue.value.clear = true;
} }
if (fitTimer) {
clearTimeout(fitTimer);
fitTimer = null;
}
} }
onMounted(async () => { onMounted(async () => {
@@ -271,6 +298,11 @@ onUnmounted(() => {
// 销毁地图实例 // 销毁地图实例
map = null; map = null;
if (fitTimer) {
clearTimeout(fitTimer);
fitTimer = null;
}
}); });
</script> </script>

View File

@@ -64,6 +64,7 @@ import { flowHisTaskList } from '@/api/workflow/instance';
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
import { listByIds } from '@/api/system/oss'; import { listByIds } from '@/api/system/oss';
import FlowChart from '@/components/Process/flowChart.vue'; import FlowChart from '@/components/Process/flowChart.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_task_status } = toRefs<any>(proxy?.useDict('wf_task_status')); const { wf_task_status } = toRefs<any>(proxy?.useDict('wf_task_status'));
const props = defineProps({ const props = defineProps({
@@ -101,9 +102,8 @@ const init = async (businessId: string | number) => {
} }
}); });
}; };
const getIds = async (ids: string | number) => { const getIds = async (ids: string) => {
const res = await listByIds(ids); return listByIds(ids);
return res;
}; };
/** 下载按钮操作 */ /** 下载按钮操作 */

View File

@@ -0,0 +1,160 @@
<template>
<div class="step-timeline">
<div v-for="(item, index) in steps" :key="index" class="step-item" :class="item.status">
<div class="step-node">
<div class="step-circle">
<el-icon v-if="item.status === 'completed'" :size="12"><Check /></el-icon>
<span v-else>{{ index + 1 }}</span>
</div>
<div v-if="index > 0" class="step-line"></div>
</div>
<div class="step-content">
<div class="step-title">
<slot name="title" :item="item" :index="index"></slot>
</div>
<div v-if="item.wrap === true">
<div class="step-subtitle">
<slot name="subtitle" :item="item" :index="index"></slot>
</div>
<div class="step-time">
<slot name="time" :item="item" :index="index"></slot>
</div>
</div>
<div v-else>
<span>
<slot name="subtitle" :item="item" :index="index"></slot>
</span>
&nbsp;&nbsp;
<span>
<slot name="time" :item="item" :index="index"></slot>
</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { Check } from '@element-plus/icons-vue';
export interface StepItem {
status: 'completed' | 'pending' | 'waiting';
title: string;
subtitle: string;
time: string;
wrap: boolean;
}
defineProps<{
steps: StepItem[];
}>();
</script>
<style lang="scss" scoped>
.step-timeline {
display: flex;
flex-direction: column-reverse;
padding: 0;
height: 100%;
}
.step-item {
flex: 1;
display: flex;
align-content: flex-start;
gap: 16px;
}
.step-node {
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
}
.step-circle {
width: 16px;
height: 16px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 800;
flex-shrink: 0;
z-index: 1;
}
.step-line {
width: 2px;
flex: 1;
min-height: 20px;
margin-top: 4px;
}
/* 已完成 */
.step-item.completed {
color: #409eff;
.step-circle {
background-color: #fff;
color: #409eff;
border: 1px solid #409eff;
}
.step-line {
background-color: #409eff;
}
}
/* 进行中 */
.step-item.pending {
color: #409eff;
.step-circle {
background-color: #409eff;
color: #fff;
}
.step-line {
background-color: #409eff;
}
}
/* 未完成 */
.step-item.waiting {
color: #cccccc;
.step-circle {
background-color: #fff;
color: #cccccc;
border: 1px solid #cccccc;
}
.step-line {
border-left: 2px dashed #cccccc;
background: none;
}
}
.step-content {
padding-bottom: 4px;
min-height: 28px;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.step-title {
font-size: 14px;
font-weight: 500;
line-height: 28px;
}
.step-subtitle {
font-size: 13px;
line-height: 22px;
margin-top: 2px;
}
.step-time {
font-size: 12px;
line-height: 20px;
margin-top: 2px;
}
</style>

View File

@@ -161,7 +161,7 @@ watch(
position: relative; position: relative;
background: #dfedff; background: #dfedff;
background: transparent; background: transparent;
border-bottom: 1px solid #dfedff; //border-bottom: 1px solid #dfedff;
color: #c0c4cc; color: #c0c4cc;

View File

@@ -3,10 +3,11 @@
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }"> <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
<side-bar class="sidebar-container" /> <side-bar class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container" id="main-container"> <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container" id="main-container">
<div> <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>
<settings ref="settingRef" /> <settings ref="settingRef" />
</div> </div>
</div> </div>
@@ -23,9 +24,12 @@ 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 { NavTypeEnum } from '@/enums/NavTypeEnum';
import { useBgColor } from '@/store/modules/bgColor';
import { initWebSocket } from '@/utils/websocket'; import { initWebSocket } from '@/utils/websocket';
import { initSSE } from '@/utils/sse'; import { initSSE } from '@/utils/sse';
const bgColorStore = useBgColor();
// ========================================== // ==========================================
// ✅ 第2步缩放逻辑从旧 script 移过来) // ✅ 第2步缩放逻辑从旧 script 移过来)
// ========================================== // ==========================================
@@ -120,7 +124,6 @@ const setLayout = () => {
<style lang="scss" scoped> <style lang="scss" scoped>
@use '@/assets/styles/mixin.scss'; @use '@/assets/styles/mixin.scss';
@use '@/assets/styles/variables.module.scss' as *; @use '@/assets/styles/variables.module.scss' as *;
//8^▐╬╬2╬╬>╘
.app-wrapper { .app-wrapper {
@include mixin.clearfix; @include mixin.clearfix;
position: relative; position: relative;
@@ -139,6 +142,10 @@ const setLayout = () => {
background-color: #dfedff; background-color: #dfedff;
} }
.navbarContant {
position: relative;
z-index: 100;
}
.drawer-bg { .drawer-bg {
background: #000; background: #000;
opacity: 0.3; opacity: 0.3;
@@ -170,4 +177,17 @@ const setLayout = () => {
.mobile .fixed-header { .mobile .fixed-header {
width: 100%; width: 100%;
} }
.bgColor {
width: 850px;
height: 850px;
background: #fdf1ee;
filter: blur(200px);
display: block;
position: absolute;
top: -50%;
right: -10%;
pointer-events: none;
z-index: 1;
border-radius: 50%;
}
</style> </style>

View File

@@ -0,0 +1,13 @@
import { defineStore } from 'pinia';
export const useBgColor = defineStore('backgroundColor', () => {
const isShow = ref(false);
const toggleBgColor = (bol: boolean = false) => {
isShow.value = bol;
console.log(isShow.value);
};
return {
toggleBgColor,
isShow
};
});

View File

@@ -83,6 +83,14 @@ export function convertToChineseCapital(num: number | string): string {
return result; return result;
} }
/**
* 标准化为Big实例空/undefined/null转0
* @param num 数字 | 数字字符串
*/
function toBig(num: string | number | undefined | null): Big {
if (num === null || num === undefined || num === '') return new Big(0);
return new Big(num);
}
/** /**
* 金额加法 * 金额加法
* @param a 数值 * @param a 数值
@@ -93,56 +101,46 @@ export function moneyAdd(a: number | string = 0, b: number | string = 0): string
return new Big(a).plus(b).toFixed(2); return new Big(a).plus(b).toFixed(2);
} }
// 金额减法 /**
export function sub(a, b) { * 减法 a - b
let r1, r2; */
try { export function sub(a: string | number, b: string | number): number {
r1 = a.toString().split('.')[1].length; return Number(toBig(a).minus(toBig(b)));
} catch (e) {
r1 = 0;
}
try {
r2 = b.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
const m = Math.pow(10, Math.max(r1, r2));
return (a * m - b * m) / m;
} }
// 金额乘法 /**
export function mul(a, b) { * 乘法 a * b
let m = 0; */
const s1 = a.toString(), export function mul(a: string | number, b: string | number): number {
s2 = b.toString(); return Number(toBig(a).times(toBig(b)));
try {
m += s1.split('.')[1].length;
} catch (e) {}
try {
m += s2.split('.')[1].length;
} catch (e) {}
return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m);
} }
// 金额除法 /**
export function div(a, b) { * 除法 a / b
let t1 = 0, * @throws 除数为0抛异常业务try catch捕获
t2 = 0; */
try { export function div(a: string | number, b: string | number): number {
t1 = a.toString().split('.')[1].length; const bigB = toBig(b);
} catch (e) {} if (bigB.eq(0)) throw new Error('除数不能为0');
try { return Number(toBig(a).div(bigB));
t2 = b.toString().split('.')[1].length;
} catch (e) {}
const x = Number(a.toString().replace('.', ''));
const y = Number(b.toString().replace('.', ''));
return (x / y) * Math.pow(10, t2 - t1);
} }
// 保留2位小数金额专用 // 保留2位小数金额专用
export function toFixed2(val) { export function toFixed2(val) {
return Number(val).toFixed(2); return Number(val).toFixed(2);
} }
// 判断 a 和 b 是否数值相等
export function eq(a: string | number, b: string | number): boolean {
return toBig(a).eq(toBig(b));
}
/**
* 金额保留指定位小数四舍五入默认2位
* @param num 计算结果
* @param digit 保留位数
*/
export function toFixedMoney(num: string | number, digit = 2): number {
return Number(toBig(num).toFixed(digit, Big.roundHalfUp));
}
/** /**
* 智能金额格式化(带单位:元/万元/亿元) * 智能金额格式化(带单位:元/万元/亿元)

View File

@@ -216,7 +216,7 @@ function headerToken() {
Authorization: 'Bearer ' + useStore.token Authorization: 'Bearer ' + useStore.token
}; };
} }
const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'];
/** 检测文件大小和格式 */ /** 检测文件大小和格式 */
function handleBeforeUpload(file: UploadFile): Promise<boolean | { file: UploadFile; type: string }> { function handleBeforeUpload(file: UploadFile): Promise<boolean | { file: UploadFile; type: string }> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -226,7 +226,7 @@ function handleBeforeUpload(file: UploadFile): Promise<boolean | { file: UploadF
type: '文件大小错误', type: '文件大小错误',
file: file file: file
}); });
} else if (file.raw.type !== 'image/jpeg' && file.raw.type !== 'image/png') { } else if (!allowImgSuffix.includes(file.raw.type)) {
reject({ reject({
type: '文件类型错误', type: '文件类型错误',
file: file file: file
@@ -355,7 +355,7 @@ const cancelForm = () => {
<style scoped lang="scss"> <style scoped lang="scss">
.box { .box {
background: linear-gradient(180deg, #deedff 0%, #f8f9fe 50%); background: #deedff;
.header { .header {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -55,7 +55,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import defaultVilageinfoImage from '@/assets/images/villageimage.png'; import defaultVilageinfoImage from '@/assets/images/villageimage.png';
import Navbar from '@/layout/components/Navbar.vue'; import Navbar from '@/layout/components/Navbar.vue';
import { deleteVillageByIdAPI, getCommunitylistAPI, switchVillageAPI } from '@/api/system/community/index'; import { deleteVillageByIdAPI, getCommunitylistAPI, switchVillageAPI } from '@/api/system/community';
import type { communitylist_rows_type } from '@/api/system/community/type'; import type { communitylist_rows_type } from '@/api/system/community/type';
import { AddLocation, Picture } from '@element-plus/icons-vue'; import { AddLocation, Picture } from '@element-plus/icons-vue';
import { useHouseStore } from '@/store/modules/house'; import { useHouseStore } from '@/store/modules/house';
@@ -119,47 +119,35 @@ function handleclick(item: communitylist_rows_type) {
<style scoped lang="scss"> <style scoped lang="scss">
.box { .box {
background: linear-gradient(180deg, #deedff 0%, #f8f9fe 50%); background: linear-gradient(180deg, #deedff 0%, #f8f9fe 50%);
position: relative;
.header { .header {
display: flex; display: flex;
align-items: center; align-items: center;
height: 50px; height: 50px;
line-height: 50px; line-height: 50px;
background: transparent; background: transparent;
position: relative;
z-index: 100;
& > div { & > div {
flex: 1; flex: 1;
} }
& > span { & > span {
padding-left: 20px; padding-left: 20px;
font-size: 20px; font-size: 20px;
color: white;
color: #000; color: #000;
} }
} }
.bgcolor { .bgcolor {
width: 1085px; width: 571px;
height: 571px; height: 571px;
background: #fdf1ee; background: #fdf1ee;
filter: blur(400px); filter: blur(200px);
display: block; display: block;
position: absolute; position: absolute;
top: 0; z-index: 90;
transform: translateY(-50%); top: -210px;
right: 0; right: 0;
pointer-events: none; /* 🔥 让它不拦截鼠标点击,穿透到下面 */
@media (max-width: 1540px) {
width: 800px;
height: 400px;
}
@media (max-width: 1280px) {
width: 600px;
height: 300px;
}
@media (max-width: 1030px) {
width: 400px;
height: 200px;
}
} }
} }
.image-slot { .image-slot {
@@ -172,6 +160,8 @@ function handleclick(item: communitylist_rows_type) {
background: #fff; background: #fff;
} }
.containerbox { .containerbox {
position: relative;
z-index: 100;
width: 85%; width: 85%;
margin: 0 auto; margin: 0 auto;
padding: 20px; padding: 20px;
@@ -208,9 +198,7 @@ function handleclick(item: communitylist_rows_type) {
.communityBody { .communityBody {
padding: 20px; padding: 20px;
color: #000; color: #000;
display: flex;
flex-wrap: wrap; flex-wrap: wrap;
display: grid; display: grid;
grid-template-columns: repeat(5, 1fr); grid-template-columns: repeat(5, 1fr);
@media (max-width: 2350px) { @media (max-width: 2350px) {
@@ -267,7 +255,6 @@ function handleclick(item: communitylist_rows_type) {
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
flex: 1; flex: 1;
text-align: center; text-align: center;
color: #4791ff;
cursor: pointer; cursor: pointer;
line-height: 60px; line-height: 60px;
font-weight: 500; font-weight: 500;

View File

@@ -113,6 +113,7 @@
:show-file-list="false" :show-file-list="false"
:on-success="handleAvatarSuccess" :on-success="handleAvatarSuccess"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:before-upload="handleBeforeUpload"
:on-error="handleError" :on-error="handleError"
:limit="1" :limit="1"
> >
@@ -310,7 +311,15 @@ const handleExceed: UploadProps['onExceed'] = (files) => {
const handleError: UploadProps['onError'] = (error: Error) => { const handleError: UploadProps['onError'] = (error: Error) => {
console.log(error); console.log(error);
}; };
const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'];
/** 检测文件大小和格式 */
const handleBeforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
if (!allowImgSuffix.includes(rawFile.name.split('.')[1])) {
ElMessage.error('文件类型错误,请重新上传');
return false;
}
return true;
};
// ! 投诉 ==================================================================================================== // ! 投诉 ====================================================================================================
// ! 签到 ======================================= // ! 签到 =======================================

View File

@@ -62,9 +62,10 @@ function headerToken() {
Authorization: 'Bearer ' + useStore.token Authorization: 'Bearer ' + useStore.token
}; };
} }
const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'];
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => { const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
const type = rawFile.type.split('/').pop(); const type = rawFile.type.split('/').pop();
if (['png', 'jpg', 'jpeg'].includes(type)) { if (allowImgSuffix.includes(type)) {
if (rawFile.size / 1024 / 1024 <= 10) { if (rawFile.size / 1024 / 1024 <= 10) {
return true; return true;
} else { } else {

View File

@@ -14,35 +14,35 @@
<div class="carInfoBox"> <div class="carInfoBox">
<div class="carInfo_item"> <div class="carInfo_item">
<div class="label">投诉类型</div> <div class="label">投诉类型</div>
<div class="main">{{ form.type }}</div> <div class="main">{{ formInfo.type }}</div>
</div> </div>
<div class="carInfo_item"> <div class="carInfo_item">
<div class="label">投诉人姓名</div> <div class="label">投诉人姓名</div>
<div class="main">{{ form.complaintName }}</div> <div class="main">{{ formInfo.complaintName }}</div>
</div> </div>
<div class="carInfo_item"> <div class="carInfo_item">
<div class="label">手机号码</div> <div class="label">手机号码</div>
<div class="main">{{ form.phone }}</div> <div class="main">{{ formInfo.phone }}</div>
</div> </div>
<div class="carInfo_item"> <div class="carInfo_item">
<div class="label">投诉时间</div> <div class="label">投诉时间</div>
<div class="main">{{ form.createTime }}</div> <div class="main">{{ formInfo.createTime }}</div>
</div> </div>
<div class="carInfo_item"> <div class="carInfo_item">
<div class="label">处理状态</div> <div class="label">处理状态</div>
<div class="main"> <div class="main">
<DictTag :options="com_complaint_status" :value="form.status"></DictTag> <DictTag :options="com_complaint_status" :value="formInfo.status"></DictTag>
</div> </div>
</div> </div>
</div> </div>
<div class="carInfoBox signLe"> <div class="carInfoBox signLe">
<div class="carInfo_item"> <div class="carInfo_item">
<div class="label">问题描述</div> <div class="label">问题描述</div>
<div class="main">{{ form.problemDes }}</div> <div class="main">{{ formInfo.problemDes }}</div>
</div> </div>
</div> </div>
<div class="carInfoBox signLe"> <div class="carInfoBox signLe">
@@ -75,14 +75,14 @@
<el-form ref="formRef" :rules="rules" class="m-auto" :inline="false" label-width="100px" :model="form"> <el-form ref="formRef" :rules="rules" class="m-auto" :inline="false" label-width="100px" :model="form">
<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">
<el-radio value="2">处理完成</el-radio> <el-radio value="1">处理完成</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="处理人员" prop="handleId"> <el-form-item label="处理人员" prop="handleId">
<repairUser <repairUser
:is-multiple="false" :is-multiple="false"
:username="form.handleName" :username="formInfo.handleName"
:userid="form.handleId ?? ''" :userid="formInfo.handleId"
returnvalue="*" returnvalue="*"
@change="handleChange" @change="handleChange"
></repairUser> ></repairUser>
@@ -91,7 +91,7 @@
<el-input type="textarea" :rows="5" v-model="form.handleContent"></el-input> <el-input type="textarea" :rows="5" v-model="form.handleContent"></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" v-if="form.status === '0'" @click="submitForm">提交</el-button> <el-button type="primary" v-if="form.status !== '0'" @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>
@@ -115,7 +115,7 @@ const { com_complaint_status } = toRefs<any>(proxy?.useDict('com_complaint_statu
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const form = ref({ const formInfo = ref({
'id': null, 'id': null,
'type': '', 'type': '',
'typeId': null, 'typeId': null,
@@ -129,24 +129,34 @@ const form = ref({
'handleContent': '', 'handleContent': '',
'createTime': null 'createTime': null
}); });
const form = ref({
'id': null,
'status': '1',
'handleId': '',
'handleName': '',
'handleContent': ''
});
const rules = { const rules = {
status: [{ required: true, message: '选择投诉处理状态', trigger: 'blur' }], status: [{ required: true, message: '选择投诉处理状态', trigger: 'blur' }],
handleContent: [{ required: true, message: '投诉处理内容不能为空', trigger: 'blur' }] handleContent: [{ required: true, message: '投诉处理内容不能为空', trigger: 'blur' }],
handleId: [{ required: true, message: '投诉处理人员不能为空', trigger: 'blur' }]
}; };
const userid = ref(); const userid = ref();
const problemImageUrlList = ref([]); const problemImageUrlList = ref([]);
// ! 投诉 ==================================================================================================== // ! 投诉 ====================================================================================================
async function init() { async function init() {
const res2 = await listComplaintType(); const res2 = await listComplaintType();
getComplaint(userid.value).then((res) => { getComplaint(userid.value).then((res) => {
form.value = { formInfo.value = {
...form.value, ...formInfo.value,
...res.data ...res.data
}; };
console.log(formInfo.value);
problemImageUrlList.value = res.data.problemImageUrl ? res.data.problemImageUrl.split(',') : []; problemImageUrlList.value = res.data.problemImageUrl ? res.data.problemImageUrl.split(',') : [];
const find = res2.rows.find((item) => item.id === form.value.typeId); const find = res2.rows.find((item) => item.id === formInfo.value.typeId);
if (find) { if (find) {
form.value.type = find.name; formInfo.value.type = find.name;
} }
}); });
} }
@@ -168,7 +178,12 @@ const submitForm = () => {
formRef.value?.validate(async (valid: boolean) => { formRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
if (userid.value) { if (userid.value) {
updateComplaint(form.value).then(() => { form.value.id = userid.value;
const obj = {
...formInfo.value,
...form.value
};
updateComplaint(obj).then(() => {
ElMessage.success('编辑成功'); ElMessage.success('编辑成功');
handleBack(); handleBack();
}); });

View File

@@ -63,7 +63,6 @@ import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { addDecoration, getDecoration, updateDecoration } from '@/api/system/Decoration'; import { addDecoration, getDecoration, updateDecoration } from '@/api/system/Decoration';
import { useHouseStore } from '@/store/modules/house'; import { useHouseStore } from '@/store/modules/house';
import { validator } from '@/utils/Reg';
const houseStore = useHouseStore(); const houseStore = useHouseStore();
const treedata = ref([]); const treedata = ref([]);

View File

@@ -68,6 +68,7 @@
:show-file-list="true" :show-file-list="true"
:on-remove="handleAvatarRemove" :on-remove="handleAvatarRemove"
:on-change="handleProgress" :on-change="handleProgress"
:before-upload="beforeAvatarUpload"
> >
<div class="uploadBtn flex flex-items-center"> <div class="uploadBtn flex flex-items-center">
<el-icon><Upload /></el-icon> <el-icon><Upload /></el-icon>
@@ -94,7 +95,7 @@ import editor from '@/components/Editor/index.vue';
import { ref } from 'vue'; import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { addHandleLog, getHandleLog, updateHandleLog, uploadAttachments } from '@/api/system/HandleLog'; import { addHandleLog, getHandleLog, updateHandleLog, uploadAttachments } from '@/api/system/HandleLog';
import { UploadFiles, UploadProps, UploadUserFile } from 'element-plus'; import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { Upload } from '@element-plus/icons-vue'; // 确保引入图标 import { Upload } from '@element-plus/icons-vue'; // 确保引入图标
@@ -143,6 +144,8 @@ const handleAvatarRemove: UploadProps['onRemove'] = (uploadFile: UploadUserFile,
// 定义大小限制常量 (单位: MB) // 定义大小限制常量 (单位: MB)
const MAX_SINGLE_FILE_SIZE = 10 * 1024 * 1024; // 10MB in bytes const MAX_SINGLE_FILE_SIZE = 10 * 1024 * 1024; // 10MB in bytes
const MAX_TOTAL_FILE_SIZE = 20 * 1024 * 1024; // 20MB in bytes const MAX_TOTAL_FILE_SIZE = 20 * 1024 * 1024; // 20MB in bytes
// 禁止的危险文件后缀
const forbidSuffix = ['exe', 'msi', 'bat', 'cmd', 'sh', 'apk', 'dmg'];
// 添加一个锁,防止 on-change 递归调用导致重复提示 // 添加一个锁,防止 on-change 递归调用导致重复提示
const isChecking = ref(false); const isChecking = ref(false);
const handleProgress: UploadProps['onChange'] = (uploadFile: UploadUserFile, uploadFiles: UploadFiles) => { const handleProgress: UploadProps['onChange'] = (uploadFile: UploadUserFile, uploadFiles: UploadFiles) => {
@@ -225,6 +228,16 @@ const handleProgress: UploadProps['onChange'] = (uploadFile: UploadUserFile, upl
}, 100); }, 100);
}; };
const blockedExts = ['.exe', '.bat', '.cmd', '.msi'];
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
const ext = rawFile.name.toLowerCase().slice(rawFile.name.lastIndexOf('.'));
if (blockedExts.includes(ext)) {
ElMessage.warning(`禁止上传 ${ext} 文件`);
return false;
}
return true;
};
// ! 初始 ==================================================================================================== // ! 初始 ====================================================================================================
function init() { function init() {
console.log('init'); console.log('init');

View File

@@ -15,20 +15,20 @@
<el-input v-model.trim="form.noticeTitle" placeholder="请输入公告标题" /> <el-input v-model.trim="form.noticeTitle" placeholder="请输入公告标题" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <!-- <el-col :span="12">-->
<el-form-item label="作者" prop="author"> <!-- <el-form-item label="作者" prop="author">-->
<el-input v-model.trim="form.author" placeholder="请输入作者" /> <!-- <el-input v-model.trim="form.author" placeholder="请输入作者" />-->
</el-form-item> <!-- </el-form-item>-->
</el-col> <!-- </el-col>-->
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="12"> <!-- <el-col :span="12">-->
<el-form-item label="公告紧急程度" prop="urgencyLevel"> <!-- <el-form-item label="公告紧急程度" prop="urgencyLevel">-->
<el-select v-model="form.urgencyLevel" clearable placeholder="请选择公告紧急程度"> <!-- <el-select v-model="form.urgencyLevel" clearable placeholder="请选择公告紧急程度">-->
<el-option v-for="dict in com_noticepc_level" :key="dict.value" :label="dict.label" :value="Number(dict.value)" /> <!-- <el-option v-for="dict in com_noticepc_level" :key="dict.value" :label="dict.label" :value="Number(dict.value)" />-->
</el-select> <!-- </el-select>-->
</el-form-item> <!-- </el-form-item>-->
</el-col> <!-- </el-col>-->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="是否发布" prop="publishStatus"> <el-form-item label="是否发布" prop="publishStatus">
<el-radio-group v-model="form.publishStatus"> <el-radio-group v-model="form.publishStatus">

View File

@@ -36,19 +36,27 @@
<el-table v-loading="loading" border :data="noticeList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" border :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="公告标题" align="center" prop="noticeTitle" /> <el-table-column label="公告标题" align="center" prop="noticeTitle" />
<el-table-column label="作者" width="120" align="center" prop="author" /> <!-- <el-table-column label="作者" width="120" align="center" prop="author" />-->
<el-table-column label="紧急程度" align="center" prop="urgencyLevel"> <!-- <el-table-column label="紧急程度" align="center" prop="urgencyLevel">-->
<template #default="scope"> <!-- <template #default="scope">-->
<dict-tag :options="com_noticepc_level" :value="scope.row.urgencyLevel"></dict-tag> <!-- <dict-tag :options="com_noticepc_level" :value="scope.row.urgencyLevel"></dict-tag>-->
</template> <!-- </template>-->
</el-table-column> <!-- </el-table-column>-->
<el-table-column label="发布状态" align="center" prop="publishStatus"> <el-table-column label="发布状态" align="center" prop="publishStatus">
<template #default="scope"> <template #default="scope">
<dict-tag :options="com_publish_status" :value="scope.row.publishStatus"></dict-tag> <dict-tag :options="com_publish_status" :value="scope.row.publishStatus"></dict-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="已读" width="120" align="center" prop="readNum" /> <el-table-column label="已读" width="120" align="center" prop="readNum">
<el-table-column label="未读" width="120" align="center" prop="unReadNum" /> <template #default="scope">
<span>{{ scope.row.readNum ?? '--' }} </span>
</template>
</el-table-column>
<el-table-column label="未读" width="120" align="center" prop="unReadNum">
<template #default="scope">
<span>{{ scope.row.unReadNum ?? '--' }} </span>
</template>
</el-table-column>
<el-table-column label="发布人" align="center" prop="publishBy" /> <el-table-column label="发布人" align="center" prop="publishBy" />
<el-table-column label="发布日期" align="center" prop="publishTime" width="100"> <el-table-column label="发布日期" align="center" prop="publishTime" width="100">
<template #default="scope"> <template #default="scope">
@@ -57,6 +65,7 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="220" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="220" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleMain(scope.row)">详情</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['notice:notice:edit']">编辑</el-button> <el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['notice:notice:edit']">编辑</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['notice:notice:remove']">删除</el-button> <el-button link type="primary" @click="handleDelete(scope.row)" v-has-permi="['notice:notice:remove']">删除</el-button>
</template> </template>
@@ -65,6 +74,17 @@
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card> </el-card>
<el-dialog v-model="MainForm.visible" title="公告详情">
<div class="w-full h-full">
<div v-html="MainForm.content"></div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="MainForm.visible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
@@ -170,6 +190,15 @@ const handleUpdate = (row?: NoticeVO) => {
} }
}); });
}; };
const MainForm = ref({
visible: false,
content: ''
});
/** 详情按钮操作 */
const handleMain = (row: NoticeVO) => {
MainForm.value.visible = true;
MainForm.value.content = row.noticeContent;
};
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: NoticeVO) => { const handleDelete = async (row?: NoticeVO) => {

View File

@@ -84,6 +84,7 @@
list-type="picture-card" list-type="picture-card"
:on-preview="handlePictureCardPreview" :on-preview="handlePictureCardPreview"
:on-remove="handleRemove" :on-remove="handleRemove"
:before-upload="handleChangeFile"
> >
<el-icon><Plus /></el-icon> <el-icon><Plus /></el-icon>
</el-upload> </el-upload>
@@ -108,7 +109,7 @@ import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { hasFormData } from '@/utils/house'; import { hasFormData } from '@/utils/house';
import Holder from '@/components/Holder/index.vue'; import Holder from '@/components/Holder/index.vue';
import { UploadProps, UploadUserFile } from 'element-plus'; import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus';
import { addRepair, getRepair, updateRepair, uploadRepairImages } from '@/api/system/Repair'; import { addRepair, getRepair, updateRepair, uploadRepairImages } from '@/api/system/Repair';
import { gettreelistRepairProject } from '@/api/system/RepairProject'; import { gettreelistRepairProject } from '@/api/system/RepairProject';
import { useHouseStore } from '@/store/modules/house'; import { useHouseStore } from '@/store/modules/house';
@@ -201,6 +202,15 @@ const dialogVisible = ref(false);
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => { const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
console.log(uploadFile, uploadFiles); console.log(uploadFile, uploadFiles);
}; };
const blockedExts = ['.exe', '.bat', '.cmd', '.msi'];
const handleChangeFile: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
const ext = rawFile.name.toLowerCase().slice(rawFile.name.lastIndexOf('.'));
if (blockedExts.includes(ext)) {
ElMessage.warning(`禁止上传 ${ext} 文件`);
return false;
}
return true;
};
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => { const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
dialogImageUrl.value = uploadFile.url!; dialogImageUrl.value = uploadFile.url!;

View File

@@ -49,9 +49,14 @@
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="报修房屋" align="center" show-overflow-tooltip prop="houseName" /> <el-table-column label="报修房屋" align="center" show-overflow-tooltip prop="houseName" />
<el-table-column label="维修项目" align="center" prop="maintenanceItemName" /> <el-table-column label="维修项目" align="center" prop="maintenanceItemName" />
<el-table-column label="标题" align="center" prop="item" />
<el-table-column label="问题描述" align="center" prop="problem" /> <el-table-column label="问题描述" align="center" prop="problem" />
<el-table-column label="报修人" width="100" align="center" prop="residentName" /> <el-table-column label="报修人" width="100" align="center" prop="residentName" />
<el-table-column label="联系电话" width="120" align="center" prop="phone" />
<el-table-column label="维修人员" width="100" align="center" prop="repairName">
<template #default="scope">
<span>{{ scope.row.repairName ?? '---' }}</span>
</template>
</el-table-column>
<el-table-column label="预约日期" align="center" prop="orderDate" width="150"> <el-table-column label="预约日期" align="center" 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>
@@ -62,7 +67,6 @@
<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 label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleShare(scope.row)" v-has-permi="['repair:repair:query']">{{ <el-button link type="primary" @click="handleShare(scope.row)" v-has-permi="['repair:repair:query']">{{
@@ -91,11 +95,11 @@ import { checkPermi } from '@/utils/permission';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_repair_status } = toRefs<any>(proxy?.useDict('com_repair_status')); const { com_repair_status } = toRefs<any>(proxy?.useDict('com_repair_status'));
interface repairlistType extends RepairVO { interface repairListType extends RepairVO {
maintenanceItemPath?: string; maintenanceItemPath?: string;
residentName: string; residentName: string;
} }
const repairList = ref<repairlistType[]>([]); const repairList = ref<repairListType[]>([]);
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>>([]);
@@ -147,7 +151,7 @@ const data = reactive<PageData<RepairForm, RepairQuery>>({
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams } = toRefs(data);
// != 维修项目u =========================================================================== // != 维修项目u ===========================================================================
@@ -193,7 +197,7 @@ const handleAdd = () => {
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: RepairVO) => { const handleUpdate = (row?: RepairVO) => {
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
router.push({ router.push({
path: '/repair/editRepair', path: '/repair/editRepair',
@@ -203,7 +207,7 @@ const handleUpdate = async (row?: RepairVO) => {
}); });
}; };
/** 分配人员 */ /** 分配人员 */
const handleShare = (row) => { const handleShare = (row: { id: string }) => {
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
router.push({ router.push({
path: '/repair/repairMain', path: '/repair/repairMain',

View File

@@ -15,56 +15,64 @@
</template> </template>
<div class="w-full h-full CarInfoBox"> <div class="w-full h-full CarInfoBox">
<el-row class="mb-20px"> <el-row class="mb-20px">
<el-col :span="10"> <el-col :span="8">
<div class="carInfo_Item"> <div class="carInfo_Item">
<div class="label">报修房屋</div> <div class="label">报修房屋:</div>
<div class="main">{{ form.houseName }}</div> <div class="main">{{ form.houseName }}</div>
</div> </div>
</el-col> </el-col>
<el-col :span="4"></el-col> <el-col :span="8">
<el-col :span="10">
<div class="carInfo_Item"> <div class="carInfo_Item">
<div class="label">维修项目</div> <div class="label">维修项目:</div>
<div class="main">{{ form.maintenanceItemName }}</div> <div class="main">{{ form.maintenanceItemName }}</div>
</div> </div>
</el-col> </el-col>
<el-col :span="8">
<div class="carInfo_Item">
<div class="label">问题描述</div>
<div class="main">{{ form.problem }}</div>
</div>
</el-col>
</el-row> </el-row>
<el-row class="mb-20px"> <el-row class="mb-20px">
<el-col :span="10"> <el-col :span="8">
<div class="carInfo_Item"> <div class="carInfo_Item">
<div class="label">报修人</div> <div class="label">报修人:</div>
<div class="main">{{ form.residentName }}</div> <div class="main">{{ form.residentName }}</div>
</div> </div>
</el-col> </el-col>
<el-col :span="4"></el-col> <el-col :span="8">
<el-col :span="10">
<div class="carInfo_Item"> <div class="carInfo_Item">
<div class="label">手机号码</div> <div class="label">手机号码:</div>
<div class="main">{{ form.phone }}</div> <div class="main">{{ form.phone }}</div>
</div> </div>
</el-col> </el-col>
</el-row> <el-col :span="8">
<el-row class="mb-20px">
<el-col>
<div class="carInfo_Item"> <div class="carInfo_Item">
<div class="label">预约日期</div> <div class="label">预约日期:</div>
<div class="main">{{ form.orderDate }}</div> <div class="main">{{ form.orderDate }}</div>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<el-row class="mb-20px"> <el-row class="mb-20px">
<el-col> <!-- <el-col :span="8">-->
<!-- <div class="carInfo_Item">-->
<!-- <div class="label">提交人</div>-->
<!-- <div class="main">{{ form.problem }}</div>-->
<!-- </div>-->
<!-- </el-col>-->
<el-col :span="8">
<div class="carInfo_Item"> <div class="carInfo_Item">
<div class="label">问题描述</div> <div class="label">提交时间:</div>
<div class="main">{{ form.problem }}</div> <div class="main">{{ form.createTime }}</div>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<el-row v-if="form.problemImageUrl.trim() === ''"> <el-row v-if="form.problemImageUrl.trim() === ''">
<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"> <div class="main fileImagebox">
<el-image <el-image
:preview-teleported="true" :preview-teleported="true"
@@ -115,31 +123,37 @@
</div> </div>
</template> </template>
<div class="h-full w-full"> <div class="h-full w-full">
<el-steps space="108px" direction="vertical" :active="activeStop"> <!-- <el-steps space="108px" direction="vertical" :active="activeStop">-->
<el-step> <!-- <el-step>-->
<template #title> <!-- <template #title>-->
<span>已提交维护信息</span> <!-- <span>完成维修 </span>-->
</template> <!-- </template>-->
<template #description> {{ reviewInfo.submitTime ?? '' }} </template> <!-- </el-step>-->
</el-step> <!-- <el-step>-->
<el-step> <!-- <template #title>-->
<template #title> <!-- <span>{{ reviewInfo.completeStatus === '0' ? '待维修' : '已维修' }} </span>-->
<span>{{ reviewInfo.allocateStatus === '0' ? '待分配' : '已分配' }} </span> <!-- </template>-->
</template> <!-- <template #description> {{ reviewInfo.completeTime ?? '维修人员进行维修' }}</template>-->
<template #description>{{ reviewInfo.allocateTime ?? '等待物业派单中' }} </template> <!-- </el-step>-->
</el-step> <!-- <el-step>-->
<el-step> <!-- <template #title>-->
<template #title> <!-- <span>{{ reviewInfo.allocateStatus === '0' ? '待分配' : '已分配' }} </span>-->
<span>{{ reviewInfo.completeStatus === '0' ? '待维修' : '已维修' }} </span> <!-- </template>-->
</template> <!-- <template #description>{{ reviewInfo.allocateTime ?? '等待物业派单中' }} </template>-->
<template #description> {{ reviewInfo.completeTime ?? '维修人员进行维修' }}</template> <!-- </el-step>-->
</el-step> <!-- <el-step>-->
<el-step> <!-- <template #title>-->
<template #title> <!-- <span>已提交维护信息</span>-->
<span>完成维修 </span> <!-- </template>-->
</template> <!-- <template #description> {{ reviewInfo.submitTime ?? '' }} </template>-->
</el-step> <!-- </el-step>-->
</el-steps> <!-- </el-steps>-->
<StepTimeline :steps="stepList">
<template #title="{ item }">{{ item.title }}</template>
<template #subtitle="{ item }">{{ item.subtitle }}</template>
<template #time="{ item }">{{ item.time }}</template>
</StepTimeline>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
@@ -149,6 +163,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import StepTimeline, { StepItem } from '@/components/StepTimeline/index.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 { getRepair, updateRepair } from '@/api/system/Repair'; import { getRepair, updateRepair } from '@/api/system/Repair';
@@ -165,7 +180,12 @@ const activeStop = ref(0);
interface formType extends RepairVO { interface formType extends RepairVO {
problemImageUrlList: string[]; problemImageUrlList: string[];
} }
const stepList = ref<StepItem[]>([
{ status: 'completed', title: '提交维修信息', subtitle: '等待提交', time: '', wrap: false },
{ status: 'pending', title: '待分配', subtitle: '等待物业派单中', time: '', wrap: true },
{ status: 'waiting', title: '维修', subtitle: '等待维修人员维修', time: '', wrap: false },
{ status: 'waiting', title: '完成维修', subtitle: '等待填写维修记录', time: '', wrap: false }
]);
const form = ref<formType>({ const form = ref<formType>({
'id': '', 'id': '',
'houseId': '', 'houseId': '',
@@ -239,19 +259,32 @@ async function init() {
function handleReview() { function handleReview() {
if (reviewInfo.value.submitStatus === '1') { if (reviewInfo.value.submitStatus === '1') {
// 提交完成 // 提交完成
active.value = 1; stepList.value[0].status = 'completed';
activeStop.value = 1; stepList.value[0].subtitle = '提交成功';
stepList.value[1].status = 'pending';
stepList.value[1].subtitle = '等待物业派单中';
stepList.value[0].time = reviewInfo.value.submitTime;
if (reviewInfo.value.allocateStatus === '1') { if (reviewInfo.value.allocateStatus === '1') {
// 分配完成 // 分配完成
active.value = 2; stepList.value[1].status = 'completed';
activeStop.value = 2; stepList.value[1].title = '已分配';
stepList.value[1].subtitle = `${form.value.repairName ?? '---'} ${form.value.repairPhone ?? '---'}`;
stepList.value[1].time = reviewInfo.value.allocateTime;
stepList.value[2].status = 'pending';
stepList.value[2].subtitle = '等待维修人员维修';
// 维修完成 // 维修完成
if (reviewInfo.value.completeStatus === '1') { if (reviewInfo.value.completeStatus === '1') {
active.value = 4; stepList.value[2].time = reviewInfo.value.completeTime;
activeStop.value = 4; stepList.value[2].status = 'completed';
stepList.value[2].subtitle = '维修完成';
stepList.value[3].status = 'completed';
stepList.value[3].subtitle = '填写维修记录';
} }
} }
} else {
stepList.value[0].status = 'waiting';
} }
} }
@@ -260,7 +293,7 @@ if (route.query.id) {
init(); init();
} }
/** 派单 ╬>*/ /** 派单 */
function handleSend() { function handleSend() {
// 已分配 // 已分配
form.value.problemStatus = '1'; form.value.problemStatus = '1';

View File

@@ -32,7 +32,7 @@
<el-table-column label="账单名称" align="center" prop="billName" /> <el-table-column label="账单名称" align="center" prop="billName" />
<el-table-column label="收费范围" align="center" prop="chargeScope" width="100"> <el-table-column label="收费范围" align="center" prop="chargeScope" width="100">
<template #default="scope"> <template #default="scope">
<template v-if="ShowHouselist.includes(scope.row.chargeTypeId)"> <template v-if="ShowHouseList.includes(scope.row.chargeTypeId)">
<DictTag :options="com_bill_charge_scope_house" :value="scope.row.chargeScope"></DictTag> <DictTag :options="com_bill_charge_scope_house" :value="scope.row.chargeScope"></DictTag>
</template> </template>
<template v-else> <template v-else>
@@ -56,8 +56,9 @@
<span v-else>---</span> <span v-else>---</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" width="200" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleChange(scope.row)">生成账单</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['bill:bill:edit']">编辑</el-button> <el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['bill:bill:edit']">编辑</el-button>
<el-button link type="primary" @click="handleMain(scope.row)" v-has-permi="['bill:bill:query']">详情</el-button> <el-button link type="primary" @click="handleMain(scope.row)" v-has-permi="['bill:bill:query']">详情</el-button>
</template> </template>
@@ -65,6 +66,36 @@
</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" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card> </el-card>
<el-dialog v-model="BillVisible" :title="BillForm.title" width="500">
<el-form :model="BillForm" label-width="auto" style="max-width: 600px">
<el-form-item label="账单生成时间">
<el-date-picker
v-if="BillForm.chargePeriod === '0'"
v-model="BillForm.year"
type="years"
format="YYYY"
value-format="YYYY"
placeholder="选择账单生成年份"
style="width: 100%"
/>
<el-date-picker
format="YYYY-MM"
value-format="YYYY-MM"
v-else
v-model="BillForm.yearMonths"
type="months"
placeholder="选择账单生成月份"
style="width: 100%"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="BillVisible = false">关闭</el-button>
<el-button type="primary" @click="handleChangeApi"> 生成 </el-button>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
@@ -72,7 +103,7 @@
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';
import { listBill } from '@/api/system/SdbBill'; import { listBill, manualGenerateApi } from '@/api/system/SdbBill';
import { BillVO, BillQuery, BillForm } from '@/api/system/SdbBill/type'; import { BillVO, BillQuery, BillForm } from '@/api/system/SdbBill/type';
import { listChargeItem } from '@/api/system/SdbChargeItem'; import { listChargeItem } from '@/api/system/SdbChargeItem';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
@@ -92,7 +123,7 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const ShowHouselist = ['2049026484272791553', '2049026503017136129']; const ShowHouseList = ['2049026484272791553', '2049026503017136129'];
const initFormData: BillForm = { const initFormData: BillForm = {
id: undefined, id: undefined,
@@ -104,7 +135,7 @@ const initFormData: BillForm = {
endDate: undefined, endDate: undefined,
villageId: undefined, villageId: undefined,
remark: undefined, remark: undefined,
cerateTime: undefined createTime: undefined
}; };
const data = reactive<PageData<BillForm, BillQuery>>({ const data = reactive<PageData<BillForm, BillQuery>>({
form: { ...initFormData }, form: { ...initFormData },
@@ -116,7 +147,7 @@ const data = reactive<PageData<BillForm, BillQuery>>({
} }
}); });
const { queryParams, form } = toRefs(data); const { queryParams } = toRefs(data);
const chargeItems = ref([]); const chargeItems = ref([]);
function init() { function init() {
@@ -176,7 +207,7 @@ const handleUpdate = (row?: BillVO) => {
}); });
}; };
function handleMain(row) { function handleMain(row: BillVO) {
router.push({ router.push({
path: '/sdb/sdbbillMain', path: '/sdb/sdbbillMain',
query: { query: {
@@ -184,6 +215,44 @@ function handleMain(row) {
} }
}); });
} }
const BillVisible = ref(false);
const BillForm = ref<{
title: string;
billId: string;
chargePeriod: string;
yearMonths?: string[];
year?: string[];
}>({
title: '',
billId: '',
chargePeriod: '1'
});
function handleChange(row: BillVO) {
clearChangeBillForm();
BillVisible.value = true;
BillForm.value.title = row.billName;
BillForm.value.chargePeriod = row.chargePeriod;
BillForm.value.billId = row.id;
if (BillForm.value.chargePeriod === '1') {
BillForm.value['yearMonths'] = [];
} else {
BillForm.value['year'] = [];
}
}
function handleChangeApi() {
manualGenerateApi(BillForm.value).then((res) => {
ElMessage.success(res.msg);
clearChangeBillForm();
BillVisible.value = false;
});
}
function clearChangeBillForm() {
BillForm.value = {
title: '',
billId: '',
chargePeriod: '1'
};
}
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@@ -40,14 +40,14 @@
</el-col> </el-col>
<right-toolbar :show-add="false" :show-edit="false" :show-delete="false"> <right-toolbar :show-add="false" :show-edit="false" :show-delete="false">
<template #dropdown> <template #dropdown>
<el-dropdown-item @click="handleMarkqrcode" v-if="checkPermi(['equipment:addqrcode:electricitydevice'])"> <el-dropdown-item @click="handleMarkQRCode" v-if="checkPermi(['equipment:addqrcode:electricitydevice'])">
<span>批量生成条形码</span> <span>批量生成条形码</span>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click="handleMarkQrcodeZip" v-if="checkPermi(['equipment:addqrcode:electricitydevice'])"> <el-dropdown-item @click="handleMarkQrcodeZip" v-if="checkPermi(['equipment:addqrcode:electricitydevice'])">
<span>批量下载条形码</span> <span>批量下载条形码</span>
</el-dropdown-item> </el-dropdown-item>
<!-- <el-dropdown-item v-has-permi="['equipment:addqrcode:electricitydevice']"> <!-- <el-dropdown-item v-has-permi="['equipment:addqrcode:electricitydevice']">
<div @click="handleMarkqrcode">批量更新固件</div> <div @click="handleMarkQRCode">批量更新固件</div>
</el-dropdown-item> --> </el-dropdown-item> -->
</template> </template>
</right-toolbar> </right-toolbar>
@@ -171,7 +171,7 @@
</el-card> </el-card>
<!-- 添加或修改电 设备对话框 --> <!-- 添加或修改电 设备对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form v-if="dialogType === 'usedWater'" :rules="rules" ref="UsedWaterformRef" :model="UsedWaterform" label-width="100px"> <el-form v-if="dialogType === 'usedWater'" :rules="rules" ref="UsedWaterFormRef" :model="UsedWaterform" 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
@@ -198,7 +198,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-form v-else-if="dialogType === 'Upload'" ref="UploadFileformRef" :model="UploadFileform" :rules="UploadFileformRules" label-width="100px"> <el-form v-else-if="dialogType === 'Upload'" ref="UploadFileFormRef" :model="UploadFileForm" :rules="UploadFileFormRules" label-width="100px">
<el-form-item label="上传更新文件" prop="file"> <el-form-item label="上传更新文件" prop="file">
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
@@ -227,9 +227,9 @@
<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 === 'UpdateHouse'" ref="EditHouseformRef" :model="EditHouseform" :rules="EditHouseformRules" label-width="100px"> <el-form v-else-if="dialogType === 'UpdateHouse'" ref="EditHouseFormRef" :model="EditHouseForm" :rules="EditHouseFormRules" label-width="100px">
<el-form-item label="更新户号" prop="houseId"> <el-form-item label="更新户号" prop="houseId">
<el-cascader v-model="userHousepath" :options="treedata" @change="handleChange" /> <el-cascader v-model="userHousePath" :options="treeData" @change="handleChange" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="unSubmit">取消</el-button> <el-button @click="unSubmit">取消</el-button>
@@ -264,8 +264,6 @@ 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, UploadProps, UploadRawFile } from 'element-plus';
import { exportImagesToZip } from '@/utils/exportZip'; import { exportImagesToZip } from '@/utils/exportZip';
import { WaterDeviceVO } from '@/api/system/SdbWaterDevice/type';
import { waterDeviceUnbind } from '@/api/system/SdbWaterDevice';
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/electricityDevice/upload'; const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/electricityDevice/upload';
@@ -311,7 +309,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: '',
@@ -349,14 +347,14 @@ function handleEndTime(val: string) {
// ! ======================================================================================= // ! =======================================================================================
// ? 上传固件 ============================================================================================== // ? 上传固件 ==============================================================================================
const UploadFileformRef = ref(null); const UploadFileFormRef = ref(null);
const uploadRef = ref(null); const uploadRef = ref(null);
const UploadFileform = ref({ const UploadFileForm = ref({
id: '', id: '',
deviceCode: '', deviceCode: '',
file: '' file: ''
}); });
const UploadFileformRules = { const UploadFileFormRules = {
file: [{ required: true, message: '更新文件不能为空', trigger: 'blur' }] file: [{ required: true, message: '更新文件不能为空', trigger: 'blur' }]
}; };
// 携带token // 携带token
@@ -387,18 +385,18 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile)
return true; return true;
}; };
const handleAvatarSuccess: UploadProps['onSuccess'] = (response: any) => { const handleAvatarSuccess: UploadProps['onSuccess'] = (response: any) => {
UploadFileform.value.file = response.data.url; UploadFileForm.value.file = response.data.url;
}; };
// ? 上传固件 ============================================================================================== // ? 上传固件 ==============================================================================================
// * 编辑房屋 ============================================================================================== // * 编辑房屋 ==============================================================================================
// 编辑房屋 // 编辑房屋
const EditHouseformRef = ref(null); const EditHouseFormRef = ref(null);
const EditHouseformRules = { const EditHouseFormRules = {
file: [{ required: true, message: '更新文件不能为空', trigger: 'blur' }] file: [{ required: true, message: '更新文件不能为空', trigger: 'blur' }]
}; };
const EditHouseform = ref({ const EditHouseForm = ref({
id: '', id: '',
deviceCode: '', deviceCode: '',
houseId: '' houseId: ''
@@ -406,7 +404,7 @@ const EditHouseform = ref({
// * 编辑房屋 ============================================================================================== // * 编辑房屋 ==============================================================================================
/** 批量生成 条形码 */ /** 批量生成 条形码 */
function handleMarkqrcode() { function handleMarkQRCode() {
if (ids.value.length > 0) { if (ids.value.length > 0) {
ElectricityDeviceMakerQrcode(ids.value).then(() => { ElectricityDeviceMakerQrcode(ids.value).then(() => {
ElMessage.success('批量生成成功'); ElMessage.success('批量生成成功');
@@ -423,12 +421,12 @@ function handleMarkQrcodeZip() {
const matchedList = ElectricityDeviceList.value.filter((item) => { const matchedList = ElectricityDeviceList.value.filter((item) => {
return selectedIds.has(item.id) && item.url; return selectedIds.has(item.id) && item.url;
}); });
produceQRzip(matchedList); produceQRZip(matchedList);
} else { } else {
ElMessage.warning('请选择设备'); ElMessage.warning('请选择设备');
} }
} }
function produceQRzip(vals: ElectricityDeviceVO[]) { function produceQRZip(vals: ElectricityDeviceVO[]) {
const arr = vals.map((item) => { const arr = vals.map((item) => {
return { return {
name: item.deviceCode, name: item.deviceCode,
@@ -464,12 +462,12 @@ const handleQrCode = (row: ElectricityDeviceVO) => {
}); });
}; };
// * house // * house
const treedata = ref([]); const treeData = ref([]);
const houseStore = useHouseStore(); const houseStore = useHouseStore();
const userHousepath = ref<CascaderValue>([]); const userHousePath = ref<CascaderValue>([]);
function handleChange(val: CascaderValue) { function handleChange(val: CascaderValue) {
userHousepath.value = val; userHousePath.value = val;
EditHouseform.value.houseId = val[2]; EditHouseForm.value.houseId = val[2];
} }
// * house // * house
const dialogType = ref<'usedWater' | 'Upload' | 'UpdateHouse'>('usedWater'); const dialogType = ref<'usedWater' | 'Upload' | 'UpdateHouse'>('usedWater');
@@ -486,13 +484,13 @@ const handleOpenDialog = (type: 'usedWater' | 'Upload' | 'UpdateHouse', row: Ele
UsedWaterform.value.endTime = ''; UsedWaterform.value.endTime = '';
usedWaterInfo.value = true; usedWaterInfo.value = true;
} else if (type === 'Upload') { } else if (type === 'Upload') {
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 === 'UpdateHouse') { } else if (type === 'UpdateHouse') {
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();
} }
}; };
@@ -509,10 +507,10 @@ function handleUnbind(data: ElectricityDeviceVO) {
} }
function getTree() { function getTree() {
treedata.value = []; treeData.value = [];
houseStore.getCurrentVilageTreeHouse().then((res) => { houseStore.getCurrentVilageTreeHouse().then((res) => {
treedata.value = res; treeData.value = res;
userHousepath.value = []; userHousePath.value = [];
}); });
} }
@@ -526,7 +524,7 @@ const submitForm = () => {
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)
@@ -541,12 +539,12 @@ const submitForm = () => {
} }
}); });
} else if (dialogType.value === 'UpdateHouse') { } else if (dialogType.value === 'UpdateHouse') {
EditHouseformRef.value?.validate(async (valid: boolean) => { EditHouseFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
await updateElectricityDevice(EditHouseform.value).finally(() => (buttonLoading.value = false)); await updateElectricityDevice(EditHouseForm.value).finally(() => (buttonLoading.value = false));
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('操作成功');
getList(); await getList();
unSubmit(); unSubmit();
} }
}); });
@@ -571,18 +569,18 @@ const reset = () => {
startTime: '', startTime: '',
endTime: '' endTime: ''
}; };
EditHouseform.value = { EditHouseForm.value = {
houseId: '', houseId: '',
deviceCode: '', deviceCode: '',
id: '' id: ''
}; };
UploadFileform.value.file = ''; UploadFileForm.value.file = '';
// 上传 清空 // 上传 清空
UploadFileformRef.value?.resetFields(); UploadFileFormRef.value?.resetFields();
// 使用电量 清空 // 使用电量 清空
EditHouseformRef.value?.resetFields(); EditHouseFormRef.value?.resetFields();
// 使用电量 清空 // 使用电量 清空
UsedWaterformRef.value?.resetFields(); UsedWaterFormRef.value?.resetFields();
}; };
/** 搜索按钮操作 */ /** 搜索按钮操作 */

View File

@@ -170,11 +170,10 @@ function headerToken() {
}; };
} }
const userid = ref(''); const userid = ref('');
const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'];
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => { const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
const type = rawFile.type.split('/').pop(); const type = rawFile.type.split('/').pop();
console.log('type', type); if (allowImgSuffix.includes(type)) {
console.log('rawFile', rawFile);
if (['png', 'jpg', 'jpeg'].includes(type)) {
if (rawFile.size / 1024 / 1024 <= 10) { if (rawFile.size / 1024 / 1024 <= 10) {
return true; return true;
} else { } else {

View File

@@ -24,7 +24,7 @@
</el-form-item> </el-form-item>
<el-form-item label="维护人员" prop="maintainUserIds"> <el-form-item label="维护人员" prop="maintainUserIds">
<repairUser <repairUser
userType="EquipmentMaintenance" userType="repair"
:userid="checkoutuseid" :userid="checkoutuseid"
ref="repairUserRef" ref="repairUserRef"
returnvalue="*" returnvalue="*"
@@ -55,10 +55,12 @@
<el-radio v-for="(item, index) in com_equipment_archive_status" :value="item.value" :label="item.label" :key="index"></el-radio> <el-radio v-for="(item, index) in com_equipment_archive_status" :value="item.value" :label="item.label" :key="index"></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="shelfLife">
<el-input v-model.trim="form.remark" placeholder="请输入" /> <el-input v-model.trim="form.shelfLife" placeholder="请输入" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" :row="5" v-model.trim="form.remark" placeholder="请输入" />
</el-form-item> </el-form-item>
<el-form-item style="margin-top: 30px"> <el-form-item style="margin-top: 30px">
<el-button type="primary" @click="submitForm">提交</el-button> <el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="cancelForm">返回</el-button> <el-button @click="cancelForm">返回</el-button>
@@ -93,6 +95,7 @@ const form = ref({
deviceModel: '', // 型号 deviceModel: '', // 型号
area: '', // 区域 area: '', // 区域
status: '0', // 区域 status: '0', // 区域
shelfLife: '', // 设备保质期
maintainUserIds: '', // 设备维护人员 maintainUserIds: '', // 设备维护人员
remark: '' // 备注 remark: '' // 备注
}); });
@@ -118,6 +121,7 @@ function init() {
deviceCode: res.data.deviceCode, deviceCode: res.data.deviceCode,
deviceModel: res.data.deviceModel, deviceModel: res.data.deviceModel,
area: res.data.area, area: res.data.area,
shelfLife: res.data.shelfLife,
status: res.data.status, status: res.data.status,
maintainUserIds: res.data.maintainUserIds, maintainUserIds: res.data.maintainUserIds,
remark: res.data.remark remark: res.data.remark

View File

@@ -38,7 +38,7 @@
</template> </template>
<el-table v-loading="loading" border :data="deviceList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" border :data="deviceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="设备分类" align="center" prop="categoryName" /> <el-table-column label="设备分类" width="100" align="center" prop="categoryName" />
<el-table-column label="设备名称" align="center" prop="deviceName" /> <el-table-column label="设备名称" align="center" prop="deviceName" />
<el-table-column label="设备编号" align="center" prop="deviceCode" /> <el-table-column label="设备编号" align="center" prop="deviceCode" />
<el-table-column label="设备型号" align="center" prop="deviceModel" /> <el-table-column label="设备型号" align="center" prop="deviceModel" />
@@ -49,8 +49,9 @@
<dict-tag :options="com_equipment_archive_status" :value="scope.row.status"></dict-tag> <dict-tag :options="com_equipment_archive_status" :value="scope.row.status"></dict-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="保修期" align="center" prop="shelfLife" />
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip /> <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" width="180" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleMain(scope.row)" v-has-permi="['system:device:query']">查看详情</el-button> <el-button link type="primary" @click="handleMain(scope.row)" v-has-permi="['system:device:query']">查看详情</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['system:device:edit']">编辑</el-button> <el-button link type="primary" @click="handleUpdate(scope.row)" v-has-permi="['system:device:edit']">编辑</el-button>

View File

@@ -234,13 +234,13 @@ const cancelForm = () => {
.info_item { .info_item {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 15px;
.label { .label {
font-size: 15px;
width: 100px; width: 100px;
text-align: right; text-align: right;
} }
.value { .value {
font-weight: 450; font-weight: normal;
} }
} }
} }

View File

@@ -12,10 +12,10 @@
</div> </div>
<div class="addBuildingFormBody"> <div class="addBuildingFormBody">
<div class="infobox"> <div class="infobox">
<div class="info_item"> <!-- <div class="info_item">-->
<div class="label">设备分类</div> <!-- <div class="label">设备分类</div>-->
<span class="value">{{ form.categoryName }}</span> <!-- <span class="value">{{ form.categoryName }}</span>-->
</div> <!-- </div>-->
<div class="info_item"> <div class="info_item">
<div class="label">设备名称</div> <div class="label">设备名称</div>
<span class="value">{{ form.deviceName }}</span> <span class="value">{{ form.deviceName }}</span>
@@ -48,7 +48,6 @@
<div class="label">维修结果</div> <div class="label">维修结果</div>
<DictTag :options="com_equipment_repair_status" :value="form.repairResult"></DictTag> <DictTag :options="com_equipment_repair_status" :value="form.repairResult"></DictTag>
</div> </div>
<!-- TODO 维修时间 -->
<div class="info_item"> <div class="info_item">
<div class="label">维修时间</div> <div class="label">维修时间</div>
<span class="value">{{ form.repairTime }}</span> <span class="value">{{ form.repairTime }}</span>
@@ -169,16 +168,16 @@ const cancelForm = () => {
gap: 20px; gap: 20px;
margin-bottom: 20px; margin-bottom: 20px;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
font-size: 0.8rem;
.info_item { .info_item {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 15px;
.label { .label {
width: 120px; width: 120px;
text-align: right; text-align: right;
} }
.value { .value {
font-weight: 450; font-weight: normal;
} }
} }
} }

View File

@@ -8,13 +8,13 @@
<el-row> <el-row>
<el-col :span="11"> <el-col :span="11">
<el-form-item label="房屋" prop="houseId"> <el-form-item label="房屋" prop="houseId">
<el-cascader @change="handleChange" v-model="userHousepath" :options="treedata" /> <el-cascader @change="handleChange" v-model="userHousePath" :options="treeData" />
</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="rentalName"> <el-form-item label="业主姓名" prop="rentalName">
<el-input disabled v-model="form.rentalName" placeholder="请输入业主姓名" /> <el-input :disabled="HasName" v-model="form.rentalName" placeholder="请输入业主姓名" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -58,7 +58,7 @@
<el-col :span="11"> <el-col :span="11">
<el-form-item label="房屋朝向" prop="type"> <el-form-item label="房屋朝向" prop="type">
<el-select v-model.number="form.houseFace" placeholder="请选择"> <el-select v-model.number="form.houseFace" placeholder="请选择">
<el-option v-for="(item, index) in houseformat" :key="index" :label="item" :value="item" /> <el-option v-for="(item, index) in houseFormat" :key="index" :label="item" :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -94,14 +94,14 @@
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="房屋设施" prop="type"> <el-form-item label="房屋设施" prop="type">
<div class="housedevicesbox" v-if="housefacilities.length > 0"> <div class="houseDevicesBox" v-if="houseFacilities.length > 0">
<div <div
v-for="item in housefacilities" v-for="item in houseFacilities"
:key="item.id" :key="item.id"
@click="handleClick(item.id)" @click="handleClick(item.id)"
class="housedevices_item" class="houseDevices_Item"
:class="{ :class="{
active: housedeviceslist.includes(item.id) active: houseDevicesList.includes(item.id)
}" }"
> >
<span class="checked"></span> <span class="checked"></span>
@@ -128,6 +128,7 @@
:on-preview="handlePictureCardPreview" :on-preview="handlePictureCardPreview"
:on-remove="handleRemove" :on-remove="handleRemove"
:on-success="handleAvatarSuccess" :on-success="handleAvatarSuccess"
:on-change="handleChangeFile"
> >
<el-icon><Plus /></el-icon> <el-icon><Plus /></el-icon>
<template #tip> <template #tip>
@@ -178,9 +179,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue'; import PageHeader from '@/components/Pageheader/index.vue';
import { CascaderValue, UploadInstance, UploadProps, UploadUserFile } from 'element-plus'; import { CascaderValue, UploadFiles, UploadInstance, UploadProps, UploadUserFile } from 'element-plus';
import { getHousePathById, hasFormData, splitStringUrl } from '@/utils/house'; import { hasFormData, splitStringUrl } from '@/utils/house';
import { addHouseRental, getHouseOwnUser, getHouseRental, updateHouseRental, uploadHouseImage } from '@/api/system/houseRental'; import { addHouseRental, getHouseOwnUser, getHouseRental, updateHouseRental, uploadHouseImage } from '@/api/system/houseRental';
import { validator } from '@/utils/Reg'; import { validator } from '@/utils/Reg';
import { useHouseStore } from '@/store/modules/house'; import { useHouseStore } from '@/store/modules/house';
@@ -192,9 +193,6 @@ const houseStore = useHouseStore();
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/houseRental/uploadImages'; const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/houseRental/uploadImages';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_rental_method } = toRefs<any>(proxy?.useDict('com_rental_method')); const { com_rental_method } = toRefs<any>(proxy?.useDict('com_rental_method'));
const { com_house_rental_facilities } = toRefs<any>(proxy?.useDict('com_house_rental_facilities') || {});
const { com_rental_status } = toRefs<any>(proxy?.useDict('com_rental_status')); const { com_rental_status } = toRefs<any>(proxy?.useDict('com_rental_status'));
const { com_house_use } = toRefs<any>(proxy?.useDict('com_house_use')); const { com_house_use } = toRefs<any>(proxy?.useDict('com_house_use'));
@@ -202,7 +200,7 @@ const router = useRouter();
const route = useRoute(); const route = useRoute();
const formRef = ref(); const formRef = ref();
const houseformat = ['东', '西', '南', '北']; const houseFormat = ['东', '西', '南', '北'];
const form = ref({ const form = ref({
id: null, id: null,
houseId: null, houseId: null,
@@ -224,14 +222,14 @@ const form = ref({
unitNoId: null, //单元id unitNoId: null, //单元id
email: '' //邮箱 email: '' //邮箱
}); });
const userHousepath = ref<CascaderValue>([]); const userHousePath = ref<CascaderValue>([]);
const rules = ref({ const rules = ref({
houseId: [{ required: true, message: '请选择房屋', trigger: 'blur' }], houseId: [{ required: true, message: '请选择房屋', trigger: 'blur' }],
rentalName: [{ required: true, message: '请输入名称', trigger: 'blur' }], rentalName: [{ required: true, message: '请输入名称', trigger: 'blur' }],
phone: [ phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' }, { required: true, message: '请输入手机号', trigger: 'blur' },
{ {
validator: (_, val) => validator(val, 'phone'), validator: (_: any, val: string | number) => validator(val, 'phone'),
message: '请输入正确的手机号码', message: '请输入正确的手机号码',
trigger: 'blur' trigger: 'blur'
} }
@@ -239,19 +237,19 @@ const rules = ref({
}); });
// ! 编辑租赁-================================================================================= // ! 编辑租赁-=================================================================================
const userid = ref(null); const userid = ref(null);
const treedata = ref(null); const treeData = ref(null);
const housefacilities = ref([]); const houseFacilities = ref([]);
// !== 楼栋房屋 ============================================================================= // !== 楼栋房屋 =============================================================================
async function gettreedata() { async function getTreeData() {
const reslist = await listHouseFacilities(); const resList = await listHouseFacilities();
housefacilities.value = reslist.rows houseFacilities.value = resList.rows
.map((item) => { .map((item) => {
if (item.status !== '1') return item; if (item.status !== '1') return item;
return false; return false;
}) })
.filter((item) => item !== false); .filter((item) => item !== false);
treedata.value = await houseStore.getCurrentVilageTreeHouse(); treeData.value = await houseStore.getCurrentVilageTreeHouse();
if (route.query.id) { if (route.query.id) {
userid.value = route.query.id; userid.value = route.query.id;
getHouseRental(userid.value).then((res) => { getHouseRental(userid.value).then((res) => {
@@ -259,22 +257,30 @@ async function gettreedata() {
...form.value, ...form.value,
...res.data ...res.data
}; };
if (form.value.facilities) housedeviceslist.value = form.value.facilities.split(',').filter((item) => item); if (form.value.facilities) houseDevicesList.value = form.value.facilities.split(',').filter((item) => item);
if (form.value.houseImageUrl) fileList.value = splitStringUrl(form.value.houseImageUrl); if (form.value.houseImageUrl) fileList.value = splitStringUrl(form.value.houseImageUrl);
handleChange([form.value.buildingId, form.value.unitNoId, form.value.houseId]); handleChange([form.value.buildingId, form.value.unitNoId, form.value.houseId]);
}); });
} }
} }
gettreedata(); getTreeData();
const HasName = ref(false);
// 获取房屋 // 获取房屋
function handleChange(val: CascaderValue) { function handleChange(val: CascaderValue) {
userHousepath.value = val; userHousePath.value = val;
form.value.houseId = val[2]; form.value.houseId = val[2];
form.value.rentalName = '';
getHouseOwnUser(val[2]).then((res) => { if (val[2]) {
form.value.rentalName = res.msg; getHouseOwnUser(val[2]).then((res) => {
}); if (res.msg) {
form.value.rentalName = res.msg;
HasName.value = true;
} else {
HasName.value = false;
}
});
}
} }
// !== 上传文件 ============================================================================= // !== 上传文件 =============================================================================
const fileList = ref<UploadUserFile[]>([]); const fileList = ref<UploadUserFile[]>([]);
@@ -282,28 +288,39 @@ const dialogImageUrl = ref('');
const dialogVisible = ref(false); const dialogVisible = ref(false);
// 上传文件成功 // 上传文件成功
const uploadRef = ref<UploadInstance>(); const uploadRef = ref<UploadInstance>();
const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => { const handleAvatarSuccess: UploadProps['onSuccess'] = (response) => {
if (response.data && response.data.error.length > 0) { if (response.data && response.data.error.length > 0) {
ElMessage.error('图片上传失败,请稍后再试'); ElMessage.error('图片上传失败,请稍后再试');
} }
}; };
const blockedExts = ['.exe', '.bat', '.cmd', '.msi'];
const handleChangeFile = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
const ext = uploadFile.name.toLowerCase().slice(uploadFile.name.lastIndexOf('.'));
if (blockedExts.includes(ext)) {
ElMessage.warning(`禁止上传 ${ext} 文件`);
fileList.value = uploadFiles.filter((f) => f.uid !== uploadFile.uid);
return;
}
fileList.value = uploadFiles;
};
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => { const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
dialogImageUrl.value = uploadFile.url!; dialogImageUrl.value = uploadFile.url!;
dialogVisible.value = true; dialogVisible.value = true;
}; };
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => { const handleRemove: UploadProps['onRemove'] = (_, uploadFiles) => {
fileList.value = uploadFiles; fileList.value = uploadFiles;
}; };
// !== 家电 ============================================================================= // !== 家电 =============================================================================
// 家电 // 家电
const housedeviceslist = ref([]); const houseDevicesList = ref([]);
function handleClick(index: number) { function handleClick(index: number) {
if (housedeviceslist.value.includes(index)) { if (houseDevicesList.value.includes(index)) {
housedeviceslist.value = housedeviceslist.value.filter((item) => item !== index); houseDevicesList.value = houseDevicesList.value.filter((item) => item !== index);
} else { } else {
housedeviceslist.value.push(index); houseDevicesList.value.push(index);
} }
} }
@@ -314,28 +331,27 @@ const submitForm = () => {
formRef.value?.validate(async (valid: boolean) => { formRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
// 处理家电 // 处理家电
form.value.facilities = housedeviceslist.value.join(','); form.value.facilities = houseDevicesList.value.join(',');
let str = ''; let str = '';
// 判断有无图片 // 判断有无图片
if (fileList.value.length > 0) { if (fileList.value.length > 0) {
const formdata = new FormData(); const formData = new FormData();
fileList.value.forEach((item) => { fileList.value.forEach((item) => {
// 只处理 新增的图片 // 只处理 新增的图片
if (item.raw && item.raw instanceof File) { if (item.raw && item.raw instanceof File) {
formdata.append('files', item.raw); formData.append('files', item.raw);
} else { } else {
str += item.url + ','; str += item.url + ',';
} }
}); });
if (!hasFormData(formdata)) return sendForm(str); if (!hasFormData(formData)) return sendForm(str);
uploadHouseImage(formdata) uploadHouseImage(formData)
.then((res) => { .then((res) => {
if (res.data.success.length > 0) { if (res.data.success.length > 0) {
const successlist = res.data.success; const successList = res.data.success;
successlist.forEach((item) => { successList.forEach((item) => {
str += item.url + ','; str += item.url + ',';
}); });
//
form.value.houseImageUrl = str; form.value.houseImageUrl = str;
} }
if (res.data.error.length > 0) { if (res.data.error.length > 0) {
@@ -350,7 +366,7 @@ const submitForm = () => {
.catch((err) => { .catch((err) => {
if (err && err.data && err.data.error.length > 0) { if (err && err.data && err.data.error.length > 0) {
const str = err.data.error.join(','); const str = err.data.error.join(',');
err.data.error.forEach((item) => { err.data.error.forEach((item: string) => {
fileList.value = fileList.value.filter((file) => file.name !== item); fileList.value = fileList.value.filter((file) => file.name !== item);
}); });
ElMessage.error('图片:' + str + ' 上传失败!,请重新上传'); ElMessage.error('图片:' + str + ' 上传失败!,请重新上传');
@@ -380,7 +396,6 @@ const sendForm = (val: string) => {
}); });
} }
}; };
// 推出 // 推出
const cancelForm = () => { const cancelForm = () => {
router.back(); router.back();
@@ -445,13 +460,13 @@ const cancelForm = () => {
.formBody { .formBody {
width: 1050px; width: 1050px;
margin: 0 auto; margin: 0 auto;
.housedevicesbox { .houseDevicesBox {
display: grid; display: grid;
margin-bottom: 20px; margin-bottom: 20px;
gap: 10px 20px; gap: 10px 20px;
grid-template-columns: repeat(6, 1fr); grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr);
.housedevices_item { .houseDevices_Item {
width: 95px; width: 95px;
height: 35px; height: 35px;
background-color: #ccc; background-color: #ccc;

View File

@@ -29,12 +29,16 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="5">
<span style="font-size: 1.2rem">日报列表</span> <span style="font-size: 1.2rem">日报列表</span>
</el-col> </el-col>
<el-col :span="14"></el-col>
<el-col :span="5" style="text-align: right">
<el-button type="warning" @click="exportCurrentPage">导出当前页</el-button>
<el-button type="warning" @click="exportAll">导出所有</el-button>
</el-col>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="itemList"> <el-table v-loading="loading" border :data="itemList">
<el-table-column label="日期" width="100" align="center" prop="statDate" /> <el-table-column label="日期" width="100" align="center" prop="statDate" />
<el-table-column label="计划巡检任务" width="120" align="center" prop="totalPlans" /> <el-table-column label="计划巡检任务" width="120" align="center" prop="totalPlans" />
@@ -50,7 +54,7 @@
</template> </template>
<script setup name="Item" lang="ts"> <script setup name="Item" lang="ts">
import { listItem, type Request } from '@/api/system/Inspection/index'; import { listItem } from '@/api/system/Inspection';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -61,12 +65,12 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const data = reactive<{ const data = reactive({
queryParams: Request;
}>({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
queryDateEnd: '',
queryDateBegin: '',
statDate: '', statDate: '',
dataType: '1' // 1 日报 dataType: '1' // 1 日报
} }
@@ -77,6 +81,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.queryDateEnd = starDate.value[1];
loading.value = true; loading.value = true;
listItem(queryParams.value) listItem(queryParams.value)
.then((res) => { .then((res) => {
@@ -101,6 +107,26 @@ const resetQuery = () => {
handleQuery(); handleQuery();
}; };
/** 导出当前页数据 */
const exportCurrentPage = () => {
proxy?.download(
'/inspection/data/export/page',
{
...queryParams.value
},
`巡检日报_${new Date().toLocaleString()}.xlsx`
);
};
const exportAll = () => {
proxy?.download(
'/inspection/data/export',
{
...queryParams.value
},
`巡检日报_${new Date().toLocaleString()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}); });

View File

@@ -29,9 +29,14 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="5">
<span style="font-size: 1.2rem">月报列表</span> <span style="font-size: 1.2rem">月报列表</span>
</el-col> </el-col>
<el-col :span="14"> </el-col>
<el-col :span="5" style="text-align: right">
<el-button type="warning" @click="exportCurrentPage">导出当前页</el-button>
<el-button type="warning" @click="exportAll">导出所有</el-button>
</el-col>
</el-row> </el-row>
</template> </template>
@@ -50,10 +55,8 @@
</template> </template>
<script setup name="Item" lang="ts"> <script setup name="Item" lang="ts">
import { listItem, type Request } from '@/api/system/Inspection/index'; import { listItem } from '@/api/system/Inspection';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const itemList = ref<any[]>([]); const itemList = ref<any[]>([]);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
@@ -62,12 +65,13 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const starDate = ref([]); const starDate = ref([]);
const data = reactive<{ const data = reactive({
queryParams: Request;
}>({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
statDate: '',
queryDateEnd: '',
queryDateBegin: '',
dataType: '3' // 1 日报 3 月报 dataType: '3' // 1 日报 3 月报
} }
}); });
@@ -77,6 +81,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.queryDateEnd = starDate.value[1];
loading.value = true; loading.value = true;
listItem(queryParams.value) listItem(queryParams.value)
.then((res) => { .then((res) => {
@@ -100,7 +106,25 @@ const resetQuery = () => {
starDate.value = []; starDate.value = [];
handleQuery(); handleQuery();
}; };
/** 导出当前页数据 */
const exportCurrentPage = () => {
proxy?.download(
'/inspection/data/export/page',
{
...queryParams.value
},
`巡检月报_${new Date().toLocaleString()}.xlsx`
);
};
const exportAll = () => {
proxy?.download(
'/inspection/data/export',
{
...queryParams.value
},
`巡检月报_${new Date().toLocaleString()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}); });

View File

@@ -86,6 +86,7 @@ import { Check } from '@element-plus/icons-vue';
import MapWrapper from '@/components/Map/index.vue'; import MapWrapper from '@/components/Map/index.vue';
import { PointVO } from '@/api/system/InspectionPoint/type'; import { PointVO } from '@/api/system/InspectionPoint/type';
import { LngLat } from '@/components/Map/type'; import { LngLat } from '@/components/Map/type';
import { PlanVO } from '@/api/system/InspectionPlan/type';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_inspection_remind_tiime } = toRefs<any>(proxy?.useDict('com_inspection_remind_tiime')); const { com_inspection_remind_tiime } = toRefs<any>(proxy?.useDict('com_inspection_remind_tiime'));
@@ -167,7 +168,6 @@ async function init() {
} else { } else {
taskTime.value = [['', '']]; taskTime.value = [['', '']];
} }
handleChangeMap(res.data.routeIds); handleChangeMap(res.data.routeIds);
}); });
} }
@@ -181,19 +181,24 @@ function outback() {
const MapWrapperRef = ref<InstanceType<typeof MapWrapper>>(); const MapWrapperRef = ref<InstanceType<typeof MapWrapper>>();
function handleChangeMap(val: string) { function handleChangeMap(val: string) {
if (val) { if (!val) return;
MapWrapperRef.value.clearMapMarker(); MapWrapperRef.value.clearMapMarker();
const find = playlist.value.find((item) => item.id === val); const find = playlist.value.find((item) => item.id === val);
if (find.points && find.points.length > 0) { if (!find) return;
find.points.forEach((item: PointVO) => { if (find.points && find.points.length > 0) {
const angular = item.location.split(',').map(Number).filter(Boolean) as LngLat; const pointMap = new Map(find.points.map((p) => [p.id, p]));
MapWrapperRef.value.addMarker_more(angular, item.pointName, item.id); const totalPointsList = find.totalPoints.split(',').filter(Boolean);
if (totalPointsList.length > 0) {
totalPointsList.forEach((item) => {
const findItem = pointMap.get(item);
const angular = findItem.location.split(',').map(Number).filter(Boolean) as LngLat;
MapWrapperRef.value.addMarker_more(angular, findItem.pointName, findItem.id);
}); });
} }
// 巡检路线 }
if (find.pathPoints) { // 巡检路线
MapWrapperRef.value.renderSavedPath(find.pathPoints); if (find.pathPoints) {
} MapWrapperRef.value.renderSavedPath(find.pathPoints);
} }
} }

View File

@@ -69,22 +69,20 @@
</template> </template>
<script setup name="Plan" lang="ts"> <script setup name="Plan" lang="ts">
import { listPlan, delPlan, changePlanStatus } from '@/api/system/InspectionPlan/index'; import { listPlan, delPlan, changePlanStatus } from '@/api/system/InspectionPlan';
import { PlanVO, PlanQuery, PlanForm } from '@/api/system/InspectionPlan/type'; import { PlanVO, PlanQuery, PlanForm } from '@/api/system/InspectionPlan/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
import { numToWeek } from '@/utils/time'; import { numToWeek } from '@/utils/time';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_inspection_plan_setting } = toRefs<any>(proxy?.useDict('com_inspection_plan_setting'));
interface planlistType extends PlanVO { interface planListType extends PlanVO {
routeNames?: string; routeNames?: string;
inspectorNames?: string; inspectorNames?: string;
executeDayNames?: number[]; executeDayNames?: number[];
} }
const planList = ref<planlistType[]>([]); const planList = ref<planListType[]>([]);
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>>([]);
@@ -93,12 +91,6 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const planFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: PlanForm = { const initFormData: PlanForm = {
id: undefined, id: undefined,
@@ -132,7 +124,7 @@ const data = reactive<PageData<PlanForm, PlanQuery>>({
rules: {} rules: {}
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams } = toRefs(data);
/** 查询巡检计划列表 */ /** 查询巡检计划列表 */
const getList = async () => { const getList = async () => {
@@ -154,18 +146,6 @@ const getList = async () => {
}); });
}; };
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
planFormRef.value?.resetFields();
};
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
@@ -194,7 +174,7 @@ const handleAdd = () => {
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: PlanVO) => { const handleUpdate = (row?: PlanVO) => {
const _ids = row?.id || ids.value[0]; const _ids = row?.id || ids.value[0];
router.push({ router.push({
path: '/inspection/editPlan', path: '/inspection/editPlan',
@@ -220,17 +200,6 @@ const handleDelete = async (row?: PlanVO) => {
await getList(); await getList();
}; };
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'system/plan/export',
{
...queryParams.value
},
`plan_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}); });

View File

@@ -5,7 +5,7 @@
<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" label-width="100px" @submit.prevent="handleQuery" :model="queryParams" :inline="true">
<el-form-item label="执行状态" prop="statuss"> <el-form-item label="执行状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择执行状态" clearable> <el-select v-model="queryParams.status" placeholder="请选择执行状态" clearable>
<el-option v-for="(item, index) in com_inspection_task_status" :key="index" :value="item.value" :label="item.label"></el-option> <el-option v-for="(item, index) in com_inspection_task_status" :key="index" :value="item.value" :label="item.label"></el-option>
</el-select> </el-select>
@@ -77,7 +77,7 @@
</template> </template>
<script setup name="Task" lang="ts"> <script setup name="Task" lang="ts">
import { listTask, delTask } from '@/api/system/InspectionTask/index'; import { listTask, delTask } from '@/api/system/InspectionTask';
import { TaskVO, TaskQuery, TaskForm } from '@/api/system/InspectionTask/type'; import { TaskVO, TaskQuery, TaskForm } from '@/api/system/InspectionTask/type';
import { checkPermi } from '@/utils/permission'; import { checkPermi } from '@/utils/permission';
@@ -86,7 +86,6 @@ const { com_inspection_task_status } = toRefs<any>(proxy?.useDict('com_inspectio
const { com_inspection_remind_tiime } = toRefs<any>(proxy?.useDict('com_inspection_remind_tiime')); const { com_inspection_remind_tiime } = toRefs<any>(proxy?.useDict('com_inspection_remind_tiime'));
const router = useRouter(); const router = useRouter();
const taskList = ref<TaskVO[]>([]); const taskList = ref<TaskVO[]>([]);
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>>([]);
@@ -95,12 +94,6 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const taskFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: TaskForm = { const initFormData: TaskForm = {
id: undefined, id: undefined,
@@ -121,7 +114,7 @@ const data = reactive<PageData<TaskForm, TaskQuery>>({
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams } = toRefs(data);
/** 查询巡检任务列表 */ /** 查询巡检任务列表 */
const getList = async () => { const getList = async () => {
@@ -136,12 +129,6 @@ const getList = async () => {
}); });
}; };
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
taskFormRef.value?.resetFields();
};
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
@@ -169,7 +156,7 @@ const handleAdd = () => {
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: TaskVO) => { const handleUpdate = (row?: TaskVO) => {
const _ids = row?.id || ids.value[0]; const _ids = row?.id || ids.value[0];
router.push({ router.push({
path: '/inspection/editTask', path: '/inspection/editTask',

View File

@@ -29,9 +29,14 @@
<el-card class="flex-1" shadow="never"> <el-card class="flex-1" shadow="never">
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="5">
<span style="font-size: 1.2rem">周报列表</span> <span style="font-size: 1.2rem">周报列表</span>
</el-col> </el-col>
<el-col :span="14"> </el-col>
<el-col :span="5" style="text-align: right">
<el-button type="warning" @click="exportCurrentPage">导出当前页</el-button>
<el-button type="warning" @click="exportAll">导出所有</el-button>
</el-col>
</el-row> </el-row>
</template> </template>
@@ -50,7 +55,7 @@
</template> </template>
<script setup name="Item" lang="ts"> <script setup name="Item" lang="ts">
import { listItem, type Request } from '@/api/system/Inspection/index'; import { listItem } from '@/api/system/Inspection';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -61,12 +66,13 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const data = reactive<{ const data = reactive({
queryParams: Request;
}>({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
statDate: '',
queryDateBegin: '',
queryDateEnd: '',
dataType: '2' // 1 日报 2 周报 3 月报 dataType: '2' // 1 日报 2 周报 3 月报
} }
}); });
@@ -76,6 +82,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.queryDateEnd = starDate.value[1];
loading.value = true; loading.value = true;
listItem(queryParams.value) listItem(queryParams.value)
.then((res) => { .then((res) => {
@@ -99,7 +107,25 @@ const resetQuery = () => {
starDate.value = []; starDate.value = [];
handleQuery(); handleQuery();
}; };
/** 导出当前页数据 */
const exportCurrentPage = () => {
proxy?.download(
'/inspection/data/export/page',
{
...queryParams.value
},
`巡检周报_${new Date().toLocaleString()}.xlsx`
);
};
const exportAll = () => {
proxy?.download(
'/inspection/data/export',
{
...queryParams.value
},
`巡检周报_${new Date().toLocaleString()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}); });

View File

@@ -51,7 +51,6 @@
<span> <span>
{{ parkingReviewInfo.parking.residentName }} {{ parkingReviewInfo.parking.residentName }}
</span> </span>
<el-button type="primary" link style="font-size: smaller">详情</el-button>
</div> </div>
</div> </div>
</div> </div>
@@ -251,7 +250,7 @@ getInfo();
font-size: 14px; font-size: 14px;
.label { .label {
font-weight: 600; font-weight: 600;
width: 80px; width: 100px;
text-align: right; text-align: right;
padding-right: 10px; padding-right: 10px;
} }

View File

@@ -36,7 +36,7 @@
</div> --> </div> -->
<!-- <div class="userinfo_item"> <!-- <div class="userinfo_item">
<div class="title_item">注册时间</div> <div class="title_item">注册时间</div>
<span> {{ userInfo.name }} </span> <span> {{ userInfo.name }} </span>6222
</div> --> </div> -->
<div class="userinfo_item img"> <div class="userinfo_item img">
<div class="title_item">身份证正面</div> <div class="title_item">身份证正面</div>
@@ -154,9 +154,10 @@ const userInfo = ref<ResidentVO>({
buildingId: '', buildingId: '',
userId: null, userId: null,
nickName: '', nickName: '',
unitNoId: '',
'id': null, 'id': null,
'name': '', 'name': '',
'type': null, 'type': '0',
'gender': null, 'gender': null,
'status': null, 'status': null,
'idCard': '', 'idCard': '',
@@ -192,7 +193,7 @@ if (route.query.id) {
getresidentReviewlistAPI(userid.value).then((res) => { getresidentReviewlistAPI(userid.value).then((res) => {
if (res.rows[0]) { if (res.rows[0]) {
reviewInfo.value = res.rows[0]; reviewInfo.value = res.rows[0];
handleActivestaus(reviewInfo.value); handleActiveStaus(reviewInfo.value);
} }
}); });
}); });
@@ -202,7 +203,7 @@ const getReviewStatusLabel = (status: string) => {
const item = com_review.value?.find((i: any) => i.value === status); const item = com_review.value?.find((i: any) => i.value === status);
return item ? item.label : '未知状态'; return item ? item.label : '未知状态';
}; };
function handleActivestaus(rews) { function handleActiveStaus(rews) {
// 提交状态 0未提交 1已提交 // 提交状态 0未提交 1已提交
if (rews.submitStatus === '0') { if (rews.submitStatus === '0') {
active.value = 0; active.value = 0;
@@ -262,7 +263,7 @@ function updatePass(val: '0' | '1' | '2') {
} }
function update() { function update() {
UpdateResidentReviewAPI(reviewInfo.value).then((res) => { UpdateResidentReviewAPI(reviewInfo.value).then(() => {
backRoute(); backRoute();
}); });
} }

View File

@@ -62,6 +62,7 @@
:on-success="handleAvatarSuccess" :on-success="handleAvatarSuccess"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:on-error="handleError" :on-error="handleError"
:before-upload="handleBeforUpload"
:limit="1" :limit="1"
> >
<img alt="身份证正面照" v-if="form.cardImageFront" :src="form.cardImageFront" class="avatar" /> <img alt="身份证正面照" v-if="form.cardImageFront" :src="form.cardImageFront" class="avatar" />
@@ -81,6 +82,7 @@
:on-exceed="handleExceed2" :on-exceed="handleExceed2"
:on-success="handleAvatar2Success" :on-success="handleAvatar2Success"
:on-error="handleError2" :on-error="handleError2"
:before-upload="handleBeforUpload"
> >
<img alt="身份证背面照" v-if="form.cardImageBack" :src="form.cardImageBack" class="avatar" /> <img alt="身份证背面照" v-if="form.cardImageBack" :src="form.cardImageBack" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon> <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
@@ -228,6 +230,15 @@ const handleError: UploadProps['onError'] = (error: Error, uploadFile: UploadFil
const handleError2: UploadProps['onError'] = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => { const handleError2: UploadProps['onError'] = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
console.log(error); console.log(error);
}; };
const blockedExts = ['.exe', '.bat', '.cmd', '.msi'];
const handleBeforUpload = (uploadFile: UploadRawFile) => {
const ext = uploadFile.name.toLowerCase().slice(uploadFile.name.lastIndexOf('.'));
if (blockedExts.includes(ext)) {
ElMessage.warning(`禁止上传 ${ext} 文件`);
return false;
}
return true;
};
// !== 提交 ============================================================================= // !== 提交 =============================================================================
// 提交 // 提交

View File

@@ -92,9 +92,6 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<Teleport to="body">
<i v-if="open" class="bgColor"></i>
</Teleport>
</div> </div>
</template> </template>
@@ -116,9 +113,10 @@ import {
PayMentMethod PayMentMethod
} from '@/api/system/Payment'; } from '@/api/system/Payment';
import RingProgress from './compontents/RingProgress.vue'; import RingProgress from './compontents/RingProgress.vue';
import { useBgColor } from '@/store/modules/bgColor';
const BgStore = useBgColor();
const defaultDay = 30; // 默认查询近30天数据 const defaultDay = 30; // 默认查询近30天数据
const open = ref(false);
const last10Day = getPastDayDate(defaultDay); const last10Day = getPastDayDate(defaultDay);
const dataTime = ref(last10Day); const dataTime = ref(last10Day);
@@ -631,12 +629,11 @@ function reset() {
} }
getList(); getList();
onMounted(() => { onMounted(() => {
open.value = true; BgStore.toggleBgColor(true);
}); });
onUnmounted(() => { onUnmounted(() => {
open.value = false; BgStore.toggleBgColor(false);
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -60,10 +60,6 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<Teleport to="body">
<i v-if="open" class="bgColor"></i>
</Teleport>
</div> </div>
</template> </template>
@@ -73,15 +69,16 @@ import Echarts from '@/components/Echarts/index.vue';
import { EChartsOption } from 'echarts'; import { EChartsOption } from 'echarts';
import { getLastNDays } from '@/utils/time'; import { getLastNDays } from '@/utils/time';
import { get_movein_moveout_list, get_movein_user_sex, get_movein_user_type, get_person_age, user_movein_list } from '@/api/system/Personnel'; import { get_movein_moveout_list, get_movein_user_sex, get_movein_user_type, get_person_age, user_movein_list } from '@/api/system/Personnel';
import { useBgColor } from '@/store/modules/bgColor';
const BgStore = useBgColor();
const date = ref(''); const date = ref('');
const open = ref(true);
onMounted(() => { onMounted(() => {
open.value = true; BgStore.toggleBgColor(true);
}); });
onUnmounted(() => { onUnmounted(() => {
open.value = false; BgStore.toggleBgColor(false);
}); });
function reset() { function reset() {
@@ -650,7 +647,7 @@ function MoveInPersonAge() {
<style scoped lang="scss"> <style scoped lang="scss">
.personnel { .personnel {
position: relative; position: relative;
background: linear-gradient(180deg, #deedff 0%, #f8f9fe 50%); background: #deedff;
.el-button { .el-button {
width: 80px; width: 80px;
height: 32px; height: 32px;

View File

@@ -94,7 +94,7 @@ const rules = ref({
// 登录账号:必填,长度限制,通常建议增加唯一性校验(需后端配合或异步校验) // 登录账号:必填,长度限制,通常建议增加唯一性校验(需后端配合或异步校验)
userName: [ userName: [
{ required: true, message: '请输入登录账号', trigger: 'blur' }, { required: true, message: '请输入登录账号', trigger: 'blur' },
{ min: 6, max: 16, message: '长度在 6 到 16 个字符', trigger: 'blur' } { min: 1, max: 16, message: '长度在 1 到 16 个字符', trigger: 'blur' }
], ],
// 登录密码:必填,新增时必填,编辑时可选(通常逻辑),这里暂按必填处理,建议长度限制 // 登录密码:必填,新增时必填,编辑时可选(通常逻辑),这里暂按必填处理,建议长度限制
password: [ password: [

View File

@@ -179,6 +179,7 @@
:on-progress="handleFileUploadProgress" :on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess" :on-success="handleFileSuccess"
:auto-upload="false" :auto-upload="false"
:on-change="handleChangeFile"
drag drag
> >
<el-icon class="el-icon--upload"> <el-icon class="el-icon--upload">
@@ -214,6 +215,7 @@ import { checkPermi } from '@/utils/permission';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { getCommunitylistAPI } from '@/api/system/community'; import { getCommunitylistAPI } from '@/api/system/community';
import { communitylist_rows_type } from '@/api/system/community/type'; import { communitylist_rows_type } from '@/api/system/community/type';
import { UploadRawFile } from 'element-plus';
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -464,6 +466,14 @@ const handleFileSuccess = (response: any, file: UploadFile) => {
}); });
getList(); getList();
}; };
const forbidSuffix = ['exe', 'msi', 'bat', 'cmd', 'sh', 'apk', 'dmg', 'zip', 'rar', '7z', 'tar', 'gz'];
const handleChangeFile = (uploadFile: UploadFile) => {
const type = uploadFile.raw.name.split('.')[1];
if (forbidSuffix.includes(type)) {
ElMessage.warning('不允许上传exe、安装包等文件');
return;
}
};
/** 提交上传文件 */ /** 提交上传文件 */
function submitFileForm() { function submitFileForm() {

View File

@@ -38,7 +38,6 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-has-permi="['system:version:add']">新增</el-button> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-has-permi="['system:version:add']">新增</el-button>
</el-col> </el-col>
<!-- <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> -->
</el-row> </el-row>
</template> </template>
@@ -109,13 +108,17 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="渠道" prop="channel"> <el-form-item label="渠道" prop="channel">
<el-radio-group v-model="form.channel"> <el-radio-group @change="handleChangeChannel" v-model="form.channel">
<el-radio value="huawei">华为</el-radio> <el-radio v-if="form.platform !== appType.ios" value="huawei">华为</el-radio>
<el-radio value="xiaomi">小米</el-radio> <el-radio v-if="form.platform !== appType.ios" value="xiaomi">小米</el-radio>
<el-radio value="oppo">oppo</el-radio> <el-radio v-if="form.platform !== appType.ios" value="oppo">oppo</el-radio>
<el-radio value="vivo">vivo</el-radio> <el-radio v-if="form.platform !== appType.ios" value="vivo">vivo</el-radio>
<el-radio v-if="form.platform !== appType.ios" value="yyb">应用宝</el-radio>
<el-radio v-if="form.platform !== appType.ios" value="honor">荣耀</el-radio>
<el-radio value="ios">ios</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="更新日志" prop="releaseNotes"> <el-form-item label="更新日志" prop="releaseNotes">
<el-input v-model="form.releaseNotes" type="textarea" placeholder="请输入内容" /> <el-input v-model="form.releaseNotes" type="textarea" placeholder="请输入内容" />
</el-form-item> </el-form-item>
@@ -225,13 +228,13 @@ enum appType {
/** 更新包类型 */ /** 更新包类型 */
enum updatePackageType { enum updatePackageType {
/** 整包更新 */ /** 整包更新 */
apk = 'apk', apk = 'apk'
/** 热更新 */ /** 热更新 */
wgt = 'wgt' // wgt = 'wgt'
} }
enum updateType { enum updateType {
/** 强制更新 */ /** 强制更新 */
force = '0', // force = '0',
/** 可选更新 */ /** 可选更新 */
optional = '1' optional = '1'
} }
@@ -245,21 +248,21 @@ const getlastVersionlist = ref([]);
* @param {string} latest - 最新版本号字符串,格式为点分十进制(如 "1.2.4" * @param {string} latest - 最新版本号字符串,格式为点分十进制(如 "1.2.4"
* @returns {boolean} 如果当前版本小于或等于最新版本,返回 true否则返回 false * @returns {boolean} 如果当前版本小于或等于最新版本,返回 true否则返回 false
*/ */
function needUpdate(current: string, latest: string) { // function needUpdate(current: string, latest: string) {
// 将版本号字符串分割并转换为数字数组 // // 将版本号字符串分割并转换为数字数组
const c = current.split('.').map(Number); // const c = current.split('.').map(Number);
const l = latest.split('.').map(Number); // const l = latest.split('.').map(Number);
const max = Math.max(c.length, l.length); // const max = Math.max(c.length, l.length);
//
// 逐段比较版本号,若当前段小于最新段则需更新,大于则无需更新 // // 逐段比较版本号,若当前段小于最新段则需更新,大于则无需更新
for (let i = 0; i < max; i++) { // for (let i = 0; i < max; i++) {
const cv = c[i] || 0; // const cv = c[i] || 0;
const lv = l[i] || 0; // const lv = l[i] || 0;
if (cv <= lv) return true; // if (cv <= lv) return true;
if (cv > lv) return false; // if (cv > lv) return false;
} // }
return false; // return false;
} // }
/** 查询【请填写功能名称】列表 */ /** 查询【请填写功能名称】列表 */
const getList = async () => { const getList = async () => {
@@ -314,6 +317,11 @@ function handleChangePlatform() {
form.value.updateType = updateType.optional; form.value.updateType = updateType.optional;
form.value.method = updatePackageType.apk; form.value.method = updatePackageType.apk;
form.value.downloadUrl = ''; form.value.downloadUrl = '';
if (form.value.platform === 'ios') {
form.value.channel = 'ios';
handleChangeChannel(form.value.channel);
}
handleGetVersionlast(); handleGetVersionlast();
} }
@@ -326,6 +334,19 @@ function handleGetVersionlast() {
}); });
} }
const channelUrl = {
'huawei': 'tappmarket://details?id=',
'xiaomi': 'mimarket://details?id=',
'oppo': 'oppomarket://details?packagename=',
'vivo': 'vivomarket://details?id=',
'honor': 'market://details?id=',
'yyb': 'tmast://appdetails?pname=',
'ios': 'itms-apps://apps.apple.com/cn/app/id'
};
function handleChangeChannel(val: string) {
console.log(val);
form.value.downloadUrl = channelUrl[val];
}
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
if (!form.value.minVersionCode) { if (!form.value.minVersionCode) {

View File

@@ -208,9 +208,9 @@ function changeActive_index(val: number) {
changeChartConfig(0); changeChartConfig(0);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// echarts
.echartsbox { .echartsbox {
height: inherit; height: inherit;
flex: 1;
.echarts { .echarts {
margin-right: 20px; margin-right: 20px;
flex: 1; flex: 1;

View File

@@ -33,7 +33,7 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="submitQuick"> </el-button> <el-button type="primary" @click="submitQuick"> </el-button>
<el-button> </el-button> <el-button @click="close"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -171,6 +171,9 @@ function submitQuick() {
getquicklist(); getquicklist();
}); });
} }
function close() {
dialog.visible = false;
}
function delteRoute(e: MouseEvent, id: string) { function delteRoute(e: MouseEvent, id: string) {
e.stopPropagation(); e.stopPropagation();
delQuicklyshortcut(id).then(() => { delQuicklyshortcut(id).then(() => {

View File

@@ -5,7 +5,7 @@
<span class="ecarts_title">近一周报修量趋势</span> <span class="ecarts_title">近一周报修量趋势</span>
<span class="echarts_units">单位</span> <span class="echarts_units">单位</span>
</div> </div>
<div class="flex flex-items-center justify-center"> <div class="flex flex-items-center justify-center" @click="handleRoute">
<span class="echarts_all_baoxiu">全部报修</span> <span class="echarts_all_baoxiu">全部报修</span>
<i class="echarts_all_arrow"></i> <i class="echarts_all_arrow"></i>
</div> </div>
@@ -18,6 +18,13 @@
<script setup lang="ts"> <script setup lang="ts">
import repairEcharts from './repairEcharts.vue'; import repairEcharts from './repairEcharts.vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const handleRoute = () => {
router.push({
path: '/repair/repairlist'
});
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -57,6 +64,7 @@ import repairEcharts from './repairEcharts.vue';
color: #98a4b2; color: #98a4b2;
line-height: 18px; line-height: 18px;
margin-right: 7px; margin-right: 7px;
cursor: pointer;
} }
.echarts_all_arrow { .echarts_all_arrow {
display: block; display: block;

View File

@@ -50,7 +50,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { addHandleLog, uploadAttachments } from '@/api/system/HandleLog'; import { addHandleLog, uploadAttachments } from '@/api/system/HandleLog';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { UploadFiles, UploadProps, UploadUserFile } from 'element-plus'; import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus';
import { Upload } from '@element-plus/icons-vue'; import { Upload } from '@element-plus/icons-vue';
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/handleLog/uploadAttachments'; const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/handleLog/uploadAttachments';
@@ -70,13 +70,20 @@ function headerToken() {
Authorization: 'Bearer ' + useStore.token Authorization: 'Bearer ' + useStore.token
}; };
} }
const blockedExts = ['.exe', '.bat', '.cmd', '.msi'];
const handleAvatarRemove: UploadProps['onRemove'] = (uploadFile: UploadFile, uploadFiles: UploadFiles) => { const handleAvatarRemove: UploadProps['onRemove'] = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
fileList.value = uploadFiles; fileList.value = uploadFiles;
}; };
const handleProgress: UploadProps['onChange'] = (uploadFile: UploadFile, uploadFiles: UploadFiles) => { const handleProgress: UploadProps['onChange'] = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
const ext = uploadFile.name.toLowerCase().slice(uploadFile.name.lastIndexOf('.'));
if (blockedExts.includes(ext)) {
ElMessage.warning(`禁止上传 ${ext} 文件`);
fileList.value = uploadFiles.filter((f) => f.uid !== uploadFile.uid);
return;
}
fileList.value = uploadFiles; fileList.value = uploadFiles;
}; };
const btnloading = ref(false); const btnloading = ref(false);
function submitTodo() { function submitTodo() {
btnloading.value = true; btnloading.value = true;

View File

@@ -6,6 +6,7 @@
<el-col :span="18"> <el-col :span="18">
<VillageAllInfo :list="list"></VillageAllInfo> <VillageAllInfo :list="list"></VillageAllInfo>
<div class="echartsbox flex flex-items-center"> <div class="echartsbox flex flex-items-center">
<repairechartsAll></repairechartsAll>
<repairechartsAll></repairechartsAll> <repairechartsAll></repairechartsAll>
<inandoutEcharts></inandoutEcharts> <inandoutEcharts></inandoutEcharts>
</div> </div>
@@ -92,8 +93,8 @@ const list = ref([
{ name: '房屋总数量', value: 458, icon: icon2 }, { name: '房屋总数量', value: 458, icon: icon2 },
{ name: '车位总数量', value: 2539, icon: icon3 }, { name: '车位总数量', value: 2539, icon: icon3 },
{ name: '设备总数量', value: 22, icon: icon4 }, { name: '设备总数量', value: 22, icon: icon4 },
{ name: '物料总数量', value: 458, icon: icon5 },
{ name: '投诉总数量', value: 458, icon: icon6 } { name: '投诉总数量', value: 458, icon: icon6 }
// { name: '物料总数量', value: 458, icon: icon5 }
]); ]);
function getVillageinfo() { function getVillageinfo() {

View File

@@ -542,7 +542,7 @@ const reset = () => {
const handleAdd = async () => { const handleAdd = async () => {
reset(); reset();
if (queryParams.value.category != '') { if (queryParams.value.category != '') {
form.value.category = queryParams.value.category; form.value.category = queryParams.value.category as string;
} }
form.value.modelValue = 'CLASSICS'; form.value.modelValue = 'CLASSICS';
form.value.formCustom = 'N'; form.value.formCustom = 'N';