Merge branch 'master' of http://47.109.37.87:3000/by2025/admin-vben5
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
@@ -48,6 +48,7 @@
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "catalog:",
|
||||
"echarts": "^5.5.1",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pinia": "catalog:",
|
||||
|
@@ -0,0 +1,61 @@
|
||||
import type { InspectionPlanVO, InspectionPlanForm, InspectionPlanQuery } from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
import type { PageResult } from '#/api/common';
|
||||
|
||||
import { commonExport } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 查询巡检计划列表
|
||||
* @param params
|
||||
* @returns 巡检计划列表
|
||||
*/
|
||||
export function inspectionPlanList(params?: InspectionPlanQuery) {
|
||||
return requestClient.get<PageResult<InspectionPlanVO>>('/property/inspectionPlan/list', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出巡检计划列表
|
||||
* @param params
|
||||
* @returns 巡检计划列表
|
||||
*/
|
||||
export function inspectionPlanExport(params?: InspectionPlanQuery) {
|
||||
return commonExport('/property/inspectionPlan/export', params ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询巡检计划详情
|
||||
* @param id id
|
||||
* @returns 巡检计划详情
|
||||
*/
|
||||
export function inspectionPlanInfo(id: ID) {
|
||||
return requestClient.get<InspectionPlanVO>(`/property/inspectionPlan/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增巡检计划
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function inspectionPlanAdd(data: InspectionPlanForm) {
|
||||
return requestClient.postWithMsg<void>('/property/inspectionPlan', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新巡检计划
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function inspectionPlanUpdate(data: InspectionPlanForm) {
|
||||
return requestClient.putWithMsg<void>('/property/inspectionPlan', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除巡检计划
|
||||
* @param id id
|
||||
* @returns void
|
||||
*/
|
||||
export function inspectionPlanRemove(id: ID | IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/property/inspectionPlan/${id}`);
|
||||
}
|
199
apps/web-antd/src/api/property/inspectionManagement/inspectionPlan/model.d.ts
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
import type { PageQuery, BaseEntity } from '#/api/common';
|
||||
|
||||
export interface InspectionPlanVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 巡检计划名称
|
||||
*/
|
||||
planName: string;
|
||||
|
||||
/**
|
||||
* 巡检路线id
|
||||
*/
|
||||
inspectionRouteId: string | number;
|
||||
|
||||
/**
|
||||
* 巡检周期
|
||||
*/
|
||||
inspectionPlanPeriod: number;
|
||||
|
||||
/**
|
||||
* 任务提前分组
|
||||
*/
|
||||
beforeTime: number;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate: string;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
startTime: string;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
endTime: string;
|
||||
|
||||
/**
|
||||
* 签到方式(0现场定位,1现场定位)
|
||||
*/
|
||||
signType: number;
|
||||
|
||||
/**
|
||||
* 允许补检(0允许1不允许)
|
||||
*/
|
||||
canReexamine: number;
|
||||
|
||||
/**
|
||||
* 选择员工
|
||||
*/
|
||||
userId: string | number;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark: string;
|
||||
|
||||
}
|
||||
|
||||
export interface InspectionPlanForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 巡检计划名称
|
||||
*/
|
||||
planName?: string;
|
||||
|
||||
/**
|
||||
* 巡检路线id
|
||||
*/
|
||||
inspectionRouteId?: string | number;
|
||||
|
||||
/**
|
||||
* 巡检周期
|
||||
*/
|
||||
inspectionPlanPeriod?: number;
|
||||
|
||||
/**
|
||||
* 任务提前分组
|
||||
*/
|
||||
beforeTime?: number;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate?: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate?: string;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
startTime?: string;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
endTime?: string;
|
||||
|
||||
/**
|
||||
* 签到方式(0现场定位,1现场定位)
|
||||
*/
|
||||
signType?: number;
|
||||
|
||||
/**
|
||||
* 允许补检(0允许1不允许)
|
||||
*/
|
||||
canReexamine?: number;
|
||||
|
||||
/**
|
||||
* 选择员工
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface InspectionPlanQuery extends PageQuery {
|
||||
/**
|
||||
* 巡检计划名称
|
||||
*/
|
||||
planName?: string;
|
||||
|
||||
/**
|
||||
* 巡检路线id
|
||||
*/
|
||||
inspectionRouteId?: string | number;
|
||||
|
||||
/**
|
||||
* 巡检周期
|
||||
*/
|
||||
inspectionPlanPeriod?: number;
|
||||
|
||||
/**
|
||||
* 任务提前分组
|
||||
*/
|
||||
beforeTime?: number;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate?: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate?: string;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
startTime?: string;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
endTime?: string;
|
||||
|
||||
/**
|
||||
* 签到方式(0现场定位,1现场定位)
|
||||
*/
|
||||
signType?: number;
|
||||
|
||||
/**
|
||||
* 允许补检(0允许1不允许)
|
||||
*/
|
||||
canReexamine?: number;
|
||||
|
||||
/**
|
||||
* 选择员工
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
import type { InspectionRouteVO, InspectionRouteForm, InspectionRouteQuery } from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
import type { PageResult } from '#/api/common';
|
||||
|
||||
import { commonExport } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 查询巡检路线列表
|
||||
* @param params
|
||||
* @returns 巡检路线列表
|
||||
*/
|
||||
export function inspectionRouteList(params?: InspectionRouteQuery) {
|
||||
return requestClient.get<PageResult<InspectionRouteVO>>('/property/inspectionRoute/list', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出巡检路线列表
|
||||
* @param params
|
||||
* @returns 巡检路线列表
|
||||
*/
|
||||
export function inspectionRouteExport(params?: InspectionRouteQuery) {
|
||||
return commonExport('/property/inspectionRoute/export', params ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询巡检路线详情
|
||||
* @param id id
|
||||
* @returns 巡检路线详情
|
||||
*/
|
||||
export function inspectionRouteInfo(id: ID) {
|
||||
return requestClient.get<InspectionRouteVO>(`/property/inspectionRoute/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增巡检路线
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function inspectionRouteAdd(data: InspectionRouteForm) {
|
||||
return requestClient.postWithMsg<void>('/property/inspectionRoute', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新巡检路线
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function inspectionRouteUpdate(data: InspectionRouteForm) {
|
||||
return requestClient.putWithMsg<void>('/property/inspectionRoute', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除巡检路线
|
||||
* @param id id
|
||||
* @returns void
|
||||
*/
|
||||
export function inspectionRouteRemove(id: ID | IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/property/inspectionRoute/${id}`);
|
||||
}
|
49
apps/web-antd/src/api/property/inspectionManagement/inspectionRoute/model.d.ts
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { PageQuery, BaseEntity } from '#/api/common';
|
||||
|
||||
export interface InspectionRouteVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 路线名称
|
||||
*/
|
||||
routeName: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark: string;
|
||||
|
||||
}
|
||||
|
||||
export interface InspectionRouteForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 路线名称
|
||||
*/
|
||||
routeName?: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface InspectionRouteQuery extends PageQuery {
|
||||
/**
|
||||
* 路线名称
|
||||
*/
|
||||
routeName?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
@@ -1,78 +0,0 @@
|
||||
import type {
|
||||
AccessControlDeviceForm,
|
||||
AccessControlDeviceQuery,
|
||||
AccessControlDeviceVO,
|
||||
} from './model';
|
||||
|
||||
import type { ID, IDS, PageResult } from '#/api/common';
|
||||
|
||||
import { commonExport } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 查询门禁设备列表
|
||||
* @param params
|
||||
* @returns 门禁设备列表
|
||||
*/
|
||||
export function accessControlDeviceList(params?: AccessControlDeviceQuery) {
|
||||
return requestClient.get<PageResult<AccessControlDeviceVO>>(
|
||||
'/sis/accessControlDevice/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据厂商编号查询设备列表
|
||||
* @param params
|
||||
* @returns 门禁设备列表
|
||||
*/
|
||||
export function queryListByFactoryNo(factory: string) {
|
||||
return requestClient.get<AccessControlDeviceVO[]>(`/sis/accessControlDevice/list/${factory}`,);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出门禁设备列表
|
||||
* @param params
|
||||
* @returns 门禁设备列表
|
||||
*/
|
||||
export function accessControlDeviceExport(params?: AccessControlDeviceQuery) {
|
||||
return commonExport('/sis/accessControlDevice/export', params ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询门禁设备详情
|
||||
* @param id id
|
||||
* @returns 门禁设备详情
|
||||
*/
|
||||
export function accessControlDeviceInfo(id: ID) {
|
||||
return requestClient.get<AccessControlDeviceVO>(
|
||||
`/sis/accessControlDevice/${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增门禁设备
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function accessControlDeviceAdd(data: AccessControlDeviceForm) {
|
||||
return requestClient.postWithMsg<void>('/sis/accessControlDevice', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新门禁设备
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function accessControlDeviceUpdate(data: AccessControlDeviceForm) {
|
||||
return requestClient.putWithMsg<void>('/sis/accessControlDevice', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除门禁设备
|
||||
* @param id id
|
||||
* @returns void
|
||||
*/
|
||||
export function accessControlDeviceRemove(id: ID | IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/sis/accessControlDevice/${id}`);
|
||||
}
|
@@ -1,202 +0,0 @@
|
||||
import type { BaseEntity, PageQuery } from '#/api/common';
|
||||
|
||||
export interface AccessControlDeviceVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: number | string;
|
||||
|
||||
/**
|
||||
* 设备编码
|
||||
*/
|
||||
eqpNo: string;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
eqpName: string;
|
||||
|
||||
/**
|
||||
* 厂商编码
|
||||
*/
|
||||
factoryNo: string;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
eqpType: number;
|
||||
|
||||
/**
|
||||
* 接入类型(1: 平台接入,2:设备接入)
|
||||
*/
|
||||
accessType: number;
|
||||
|
||||
/**
|
||||
* 设备ip
|
||||
*/
|
||||
eqpIp: string;
|
||||
|
||||
/**
|
||||
* 设备端口
|
||||
*/
|
||||
eqpPort: number;
|
||||
|
||||
/**
|
||||
* 设备账号
|
||||
*/
|
||||
eqpAccount: string;
|
||||
|
||||
/**
|
||||
* 设备密码
|
||||
*/
|
||||
eqpPwd: string;
|
||||
|
||||
/**
|
||||
* 创建人id
|
||||
*/
|
||||
createById: number | string;
|
||||
|
||||
/**
|
||||
* 更新人id
|
||||
*/
|
||||
updateById: number | string;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
searchValue: string;
|
||||
}
|
||||
|
||||
export interface AccessControlDeviceForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: number | string;
|
||||
|
||||
/**
|
||||
* 设备编码
|
||||
*/
|
||||
eqpNo?: string;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
eqpName?: string;
|
||||
|
||||
/**
|
||||
* 厂商编码
|
||||
*/
|
||||
factoryNo?: string;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
eqpType?: number;
|
||||
|
||||
/**
|
||||
* 接入类型(1: 平台接入,2:设备接入)
|
||||
*/
|
||||
accessType?: number;
|
||||
|
||||
/**
|
||||
* 设备ip
|
||||
*/
|
||||
eqpIp?: string;
|
||||
|
||||
/**
|
||||
* 设备端口
|
||||
*/
|
||||
eqpPort?: number;
|
||||
|
||||
/**
|
||||
* 设备账号
|
||||
*/
|
||||
eqpAccount?: string;
|
||||
|
||||
/**
|
||||
* 设备密码
|
||||
*/
|
||||
eqpPwd?: string;
|
||||
|
||||
/**
|
||||
* 创建人id
|
||||
*/
|
||||
createById?: number | string;
|
||||
|
||||
/**
|
||||
* 更新人id
|
||||
*/
|
||||
updateById?: number | string;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
searchValue?: string;
|
||||
}
|
||||
|
||||
export interface AccessControlDeviceQuery extends PageQuery {
|
||||
/**
|
||||
* 设备编码
|
||||
*/
|
||||
eqpNo?: string;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
eqpName?: string;
|
||||
|
||||
/**
|
||||
* 厂商编码
|
||||
*/
|
||||
factoryNo?: string;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
eqpType?: number;
|
||||
|
||||
/**
|
||||
* 接入类型(1: 平台接入,2:设备接入)
|
||||
*/
|
||||
accessType?: number;
|
||||
|
||||
/**
|
||||
* 设备ip
|
||||
*/
|
||||
eqpIp?: string;
|
||||
|
||||
/**
|
||||
* 设备端口
|
||||
*/
|
||||
eqpPort?: number;
|
||||
|
||||
/**
|
||||
* 设备账号
|
||||
*/
|
||||
eqpAccount?: string;
|
||||
|
||||
/**
|
||||
* 设备密码
|
||||
*/
|
||||
eqpPwd?: string;
|
||||
|
||||
/**
|
||||
* 创建人id
|
||||
*/
|
||||
createById?: number | string;
|
||||
|
||||
/**
|
||||
* 更新人id
|
||||
*/
|
||||
updateById?: number | string;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
searchValue?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
@@ -1,18 +1,19 @@
|
||||
import type { AuthRecordVO, AuthRecordForm, AuthRecordQuery } from './model';
|
||||
import type { AuthRecordForm, AuthRecordQuery, AuthRecordVO } from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
import type { PageResult } from '#/api/common';
|
||||
import type { ID, IDS, PageResult } from '#/api/common';
|
||||
|
||||
import { commonExport } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 查询授权记录列表
|
||||
* @param params
|
||||
* @returns 授权记录列表
|
||||
*/
|
||||
* 查询授权记录列表
|
||||
* @param params
|
||||
* @returns 授权记录列表
|
||||
*/
|
||||
export function authRecordList(params?: AuthRecordQuery) {
|
||||
return requestClient.get<PageResult<AuthRecordVO>>('/sis/authRecord/list', { params });
|
||||
return requestClient.get<PageResult<AuthRecordVO>>('/sis/authRecord/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,3 +60,15 @@ export function authRecordUpdate(data: AuthRecordForm) {
|
||||
export function authRecordRemove(id: ID | IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/sis/authRecord/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对人员库进行授权
|
||||
* @param params id
|
||||
*/
|
||||
export function authPersonLib(params: any) {
|
||||
return requestClient.postWithMsg<void>(`/sis/authRecord/authLib`, params);
|
||||
}
|
||||
|
||||
export function queryAuthDevice(id: ID) {
|
||||
return requestClient.get<AuthRecordVO[]>(`/sis/authRecord/authDevice/${id}`);
|
||||
}
|
||||
|
@@ -15,6 +15,8 @@ export interface AuthRecordVO {
|
||||
* 门禁id
|
||||
*/
|
||||
acdId: string | number;
|
||||
|
||||
deviceId: string | number;
|
||||
}
|
||||
|
||||
export interface AuthRecordForm extends BaseEntity {
|
||||
|
@@ -1,18 +1,24 @@
|
||||
import type { DeviceManageVO, DeviceManageForm, DeviceManageQuery } from './model';
|
||||
import type {
|
||||
DeviceManageForm,
|
||||
DeviceManageQuery,
|
||||
DeviceManageVO,
|
||||
} from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
import type { PageResult } from '#/api/common';
|
||||
import type { ID, IDS, PageResult, TreeNode } from '#/api/common';
|
||||
|
||||
import { commonExport } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 查询设备管理列表
|
||||
* @param params
|
||||
* @returns 设备管理列表
|
||||
*/
|
||||
* 查询设备管理列表
|
||||
* @param params
|
||||
* @returns 设备管理列表
|
||||
*/
|
||||
export function deviceManageList(params?: DeviceManageQuery) {
|
||||
return requestClient.get<PageResult<DeviceManageVO>>('/sis/deviceManage/list', { params });
|
||||
return requestClient.get<PageResult<DeviceManageVO>>(
|
||||
'/sis/deviceManage/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,3 +65,11 @@ export function deviceManageUpdate(data: DeviceManageForm) {
|
||||
export function deviceManageRemove(id: ID | IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/sis/deviceManage/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询门禁书
|
||||
* @returns void
|
||||
*/
|
||||
export function queryTree() {
|
||||
return requestClient.get<TreeNode<Number>[]>(`/sis/deviceManage/tree`);
|
||||
}
|
||||
|
@@ -1,62 +0,0 @@
|
||||
import type { DevicePointForm, DevicePointQuery, DevicePointVO } from './model';
|
||||
|
||||
import type { ID, IDS, PageResult } from '#/api/common';
|
||||
|
||||
import { commonExport } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 查询宇视设备点位(通道)列表
|
||||
* @param params
|
||||
* @returns 宇视设备点位(通道)列表
|
||||
*/
|
||||
export function devicePointList(params?: DevicePointQuery) {
|
||||
return requestClient.get<PageResult<DevicePointVO>>('/sis/devicePoint/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出宇视设备点位(通道)列表
|
||||
* @param params
|
||||
* @returns 宇视设备点位(通道)列表
|
||||
*/
|
||||
export function devicePointExport(params?: DevicePointQuery) {
|
||||
return commonExport('/sis/devicePoint/export', params ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询宇视设备点位(通道)详情
|
||||
* @param id id
|
||||
* @returns 宇视设备点位(通道)详情
|
||||
*/
|
||||
export function devicePointInfo(id: ID) {
|
||||
return requestClient.get<DevicePointVO>(`/sis/devicePoint/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增宇视设备点位(通道)
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function devicePointAdd(data: DevicePointForm) {
|
||||
return requestClient.postWithMsg<void>('/sis/devicePoint', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新宇视设备点位(通道)
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function devicePointUpdate(data: DevicePointForm) {
|
||||
return requestClient.putWithMsg<void>('/sis/devicePoint', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除宇视设备点位(通道)
|
||||
* @param id id
|
||||
* @returns void
|
||||
*/
|
||||
export function devicePointRemove(id: ID | IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/sis/devicePoint/${id}`);
|
||||
}
|
259
apps/web-antd/src/api/sis/devicePoint/model.d.ts
vendored
@@ -1,259 +0,0 @@
|
||||
import type { BaseEntity, PageQuery } from '#/api/common';
|
||||
|
||||
export interface DevicePointVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: number | string;
|
||||
|
||||
/**
|
||||
* 设备编码
|
||||
*/
|
||||
eqpCode: string;
|
||||
|
||||
/**
|
||||
* 视频协议 1:onvif 2:
|
||||
rtsp
|
||||
*/
|
||||
video: number | string;
|
||||
|
||||
/**
|
||||
* 传输协议(AIBOX 需要,一
|
||||
体机不需要) 1: tcp 2:
|
||||
udp
|
||||
*/
|
||||
transportType: number;
|
||||
|
||||
/**
|
||||
* 点位名称
|
||||
*/
|
||||
channelName: string;
|
||||
|
||||
/**
|
||||
* rtsp 地址(当视频协议为
|
||||
rtsp 时,该字段必填)
|
||||
*/
|
||||
rtspAddr: string;
|
||||
|
||||
/**
|
||||
* 点位名称
|
||||
*/
|
||||
ip: string;
|
||||
|
||||
/**
|
||||
* 端口
|
||||
*/
|
||||
port: number;
|
||||
|
||||
/**
|
||||
* 通道相机账号
|
||||
*/
|
||||
username: string;
|
||||
|
||||
/**
|
||||
* 相机密码
|
||||
*/
|
||||
pwd: string;
|
||||
|
||||
/**
|
||||
* onvif 设备码流添加方
|
||||
式:1:主码流 2:自定
|
||||
义码流
|
||||
*/
|
||||
videoType: number | string;
|
||||
|
||||
/**
|
||||
* 码流 id:当选择自定义码
|
||||
流时,该字段必填,值为
|
||||
获取设备码流信息接口返
|
||||
回的码流 id
|
||||
*/
|
||||
videoId: number | string;
|
||||
|
||||
/**
|
||||
* 创建人id
|
||||
*/
|
||||
createById: number | string;
|
||||
|
||||
/**
|
||||
* 更新人id
|
||||
*/
|
||||
updateById: number | string;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
searchValue: string;
|
||||
}
|
||||
|
||||
export interface DevicePointForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: number | string;
|
||||
|
||||
/**
|
||||
* 设备编码
|
||||
*/
|
||||
eqpCode?: string;
|
||||
|
||||
/**
|
||||
* 视频协议 1:onvif 2:
|
||||
rtsp
|
||||
*/
|
||||
video?: number | string;
|
||||
|
||||
/**
|
||||
* 传输协议(AIBOX 需要,一
|
||||
体机不需要) 1: tcp 2:
|
||||
udp
|
||||
*/
|
||||
transportType?: number;
|
||||
|
||||
/**
|
||||
* 点位名称
|
||||
*/
|
||||
channelName?: string;
|
||||
|
||||
/**
|
||||
* rtsp 地址(当视频协议为
|
||||
rtsp 时,该字段必填)
|
||||
*/
|
||||
rtspAddr?: string;
|
||||
|
||||
/**
|
||||
* 点位名称
|
||||
*/
|
||||
ip?: string;
|
||||
|
||||
/**
|
||||
* 端口
|
||||
*/
|
||||
port?: number;
|
||||
|
||||
/**
|
||||
* 通道相机账号
|
||||
*/
|
||||
username?: string;
|
||||
|
||||
/**
|
||||
* 相机密码
|
||||
*/
|
||||
pwd?: string;
|
||||
|
||||
/**
|
||||
* onvif 设备码流添加方
|
||||
式:1:主码流 2:自定
|
||||
义码流
|
||||
*/
|
||||
videoType?: number | string;
|
||||
|
||||
/**
|
||||
* 码流 id:当选择自定义码
|
||||
流时,该字段必填,值为
|
||||
获取设备码流信息接口返
|
||||
回的码流 id
|
||||
*/
|
||||
videoId?: number | string;
|
||||
|
||||
/**
|
||||
* 创建人id
|
||||
*/
|
||||
createById?: number | string;
|
||||
|
||||
/**
|
||||
* 更新人id
|
||||
*/
|
||||
updateById?: number | string;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
searchValue?: string;
|
||||
}
|
||||
|
||||
export interface DevicePointQuery extends PageQuery {
|
||||
/**
|
||||
* 设备编码
|
||||
*/
|
||||
eqpCode?: string;
|
||||
|
||||
/**
|
||||
* 视频协议 1:onvif 2:
|
||||
rtsp
|
||||
*/
|
||||
video?: number | string;
|
||||
|
||||
/**
|
||||
* 传输协议(AIBOX 需要,一
|
||||
体机不需要) 1: tcp 2:
|
||||
udp
|
||||
*/
|
||||
transportType?: number;
|
||||
|
||||
/**
|
||||
* 点位名称
|
||||
*/
|
||||
channelName?: string;
|
||||
|
||||
/**
|
||||
* rtsp 地址(当视频协议为
|
||||
rtsp 时,该字段必填)
|
||||
*/
|
||||
rtspAddr?: string;
|
||||
|
||||
/**
|
||||
* 点位名称
|
||||
*/
|
||||
ip?: string;
|
||||
|
||||
/**
|
||||
* 端口
|
||||
*/
|
||||
port?: number;
|
||||
|
||||
/**
|
||||
* 通道相机账号
|
||||
*/
|
||||
username?: string;
|
||||
|
||||
/**
|
||||
* 相机密码
|
||||
*/
|
||||
pwd?: string;
|
||||
|
||||
/**
|
||||
* onvif 设备码流添加方
|
||||
式:1:主码流 2:自定
|
||||
义码流
|
||||
*/
|
||||
videoType?: number | string;
|
||||
|
||||
/**
|
||||
* 码流 id:当选择自定义码
|
||||
流时,该字段必填,值为
|
||||
获取设备码流信息接口返
|
||||
回的码流 id
|
||||
*/
|
||||
videoId?: number | string;
|
||||
|
||||
/**
|
||||
* 创建人id
|
||||
*/
|
||||
createById?: number | string;
|
||||
|
||||
/**
|
||||
* 更新人id
|
||||
*/
|
||||
updateById?: number | string;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
searchValue?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
BIN
apps/web-antd/src/assets/digitalIntelligence/bg.png
Normal file
After Width: | Height: | Size: 556 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/center-bg.png
Normal file
After Width: | Height: | Size: 360 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle1.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle2.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle3.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle4.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
apps/web-antd/src/assets/energyConsumptionAnalysis/bg.png
Normal file
After Width: | Height: | Size: 549 KiB |
BIN
apps/web-antd/src/assets/energyConsumptionAnalysis/center-bg.png
Normal file
After Width: | Height: | Size: 570 KiB |
After Width: | Height: | Size: 803 B |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.1 KiB |
BIN
apps/web-antd/src/assets/monitor/bg.png
Normal file
After Width: | Height: | Size: 394 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-bg1.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-bg2.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-bg3.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-button1.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor1.png
Normal file
After Width: | Height: | Size: 167 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor2.png
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor3.png
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor4.png
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor5.png
Normal file
After Width: | Height: | Size: 184 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor6.png
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
apps/web-antd/src/assets/navigation/科技数据粒子上升蓝色.gif
Normal file
After Width: | Height: | Size: 38 MiB |
BIN
apps/web-antd/src/assets/property/bg.png
Normal file
After Width: | Height: | Size: 438 KiB |
BIN
apps/web-antd/src/assets/property/center-bg.png
Normal file
After Width: | Height: | Size: 360 KiB |
BIN
apps/web-antd/src/assets/property/customer-circle.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle1.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle2.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle3.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle4.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
apps/web-antd/src/assets/security/bg.png
Normal file
After Width: | Height: | Size: 450 KiB |
BIN
apps/web-antd/src/assets/security/button1.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
apps/web-antd/src/assets/security/button2.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
apps/web-antd/src/assets/security/button3.png
Normal file
After Width: | Height: | Size: 773 B |
BIN
apps/web-antd/src/assets/security/center-bg.png
Normal file
After Width: | Height: | Size: 327 KiB |
BIN
apps/web-antd/src/assets/security/monitor1.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
apps/web-antd/src/assets/security/monitor2.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
apps/web-antd/src/assets/security/stop-icon.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
@@ -11,6 +11,7 @@ import {
|
||||
GiteeIcon,
|
||||
GitHubOutlined,
|
||||
UserOutlined,
|
||||
|
||||
} from '@vben/icons';
|
||||
import {
|
||||
BasicLayout,
|
||||
@@ -30,6 +31,7 @@ import { resetRoutes } from '#/router';
|
||||
import { useAuthStore, useNotifyStore } from '#/store';
|
||||
import { useTenantStore } from '#/store/tenant';
|
||||
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||
// import { TagOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const authStore = useAuthStore();
|
||||
@@ -83,6 +85,13 @@ const menus = computed(() => {
|
||||
icon: CircleHelp,
|
||||
text: $t('ui.widgets.qa'),
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
router.push('/navigation');
|
||||
},
|
||||
// icon: TagOutlined,
|
||||
text: '返回导航',
|
||||
},
|
||||
];
|
||||
/**
|
||||
* 租户选中状态 不显示个人中心
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { initPreferences } from '@vben/preferences';
|
||||
import { unmountGlobalLoading } from '@vben/utils';
|
||||
import { overridesPreferences } from './preferences';
|
||||
|
||||
import './utils/flexible'
|
||||
/**
|
||||
* 应用初始化完成之后再进行页面加载渲染
|
||||
*/
|
||||
|
@@ -100,6 +100,67 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/navigation/Navigation.vue'),
|
||||
name: 'navigation',
|
||||
path: '/navigation',
|
||||
meta: {
|
||||
title: '导航页面', // 或者用 $t('page.navigation.title')
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/energyConsumptionAnalysis/index.vue'),
|
||||
name: 'energyAnalysis',
|
||||
path: '/energyAnalysis',
|
||||
meta: {
|
||||
title: '能耗大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
}, {
|
||||
component: () => import('#/views/screen/property/index.vue'),
|
||||
name: 'property',
|
||||
path: '/property',
|
||||
meta: {
|
||||
title: '物业大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
}, {
|
||||
component: () => import('#/views/screen/security/index.vue'),
|
||||
name: 'security',
|
||||
path: '/security',
|
||||
meta: {
|
||||
title: '安防大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/monitor/index.vue'),
|
||||
name: 'monitor',
|
||||
path: '/monitor',
|
||||
meta: {
|
||||
title: '监控大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/security/index.vue'),
|
||||
name: 'security',
|
||||
path: '/security',
|
||||
meta: {
|
||||
title: '安防大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/digitalIntelligence/index.vue'),
|
||||
name: 'digitalIntelligence',
|
||||
path: '/digitalIntelligence',
|
||||
meta: {
|
||||
title: '商务中心数智管理平台',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export { coreRoutes, fallbackNotFoundRoute };
|
||||
|
@@ -58,7 +58,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
} else {
|
||||
onSuccess
|
||||
? await onSuccess?.()
|
||||
: await router.push(preferences.app.defaultHomePath);
|
||||
: await router.push('/navigation');
|
||||
}
|
||||
|
||||
if (userInfo?.realName) {
|
||||
|
119
apps/web-antd/src/utils/echartsResize.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { ECharts } from 'echarts'
|
||||
|
||||
/**
|
||||
* ECharts 图表自适应管理器
|
||||
* 用于监听图表容器大小变化并自动调整图表大小
|
||||
*/
|
||||
export class EChartsResizeManager {
|
||||
private charts: Map<ECharts, ResizeObserver> = new Map()
|
||||
private resizeHandler: (() => void) | null = null
|
||||
|
||||
constructor() {
|
||||
this.initResizeListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加图表到管理器
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
addChart(chart: ECharts): void {
|
||||
if (chart && !this.charts.has(chart)) {
|
||||
// 获取图表容器元素
|
||||
const container = chart.getDom()
|
||||
if (container) {
|
||||
// 为每个图表创建独立的 ResizeObserver
|
||||
const observer = new ResizeObserver(() => {
|
||||
if (chart && !chart.isDisposed()) {
|
||||
chart.resize()
|
||||
}
|
||||
})
|
||||
|
||||
// 监听图表容器的大小变化
|
||||
observer.observe(container)
|
||||
this.charts.set(chart, observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除图表
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
removeChart(chart: ECharts): void {
|
||||
const observer = this.charts.get(chart)
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
this.charts.delete(chart)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有图表
|
||||
*/
|
||||
clearCharts(): void {
|
||||
this.charts.forEach(observer => {
|
||||
observer.disconnect()
|
||||
})
|
||||
this.charts.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新调整所有图表大小
|
||||
*/
|
||||
resizeAllCharts(): void {
|
||||
this.charts.forEach((observer, chart) => {
|
||||
if (chart && !chart.isDisposed()) {
|
||||
chart.resize()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化窗口大小变化监听器
|
||||
*/
|
||||
private initResizeListener(): void {
|
||||
// 监听窗口大小变化
|
||||
this.resizeHandler = () => {
|
||||
this.resizeAllCharts()
|
||||
}
|
||||
window.addEventListener('resize', this.resizeHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁监听器
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler)
|
||||
this.resizeHandler = null
|
||||
}
|
||||
|
||||
this.clearCharts()
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
export const echartsResizeManager = new EChartsResizeManager()
|
||||
|
||||
/**
|
||||
* 便捷函数:添加图表到管理器
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
export function addChartToResizeManager(chart: ECharts): void {
|
||||
echartsResizeManager.addChart(chart)
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷函数:移除图表
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
export function removeChartFromResizeManager(chart: ECharts): void {
|
||||
echartsResizeManager.removeChart(chart)
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷函数:手动触发所有图表重新调整大小
|
||||
*/
|
||||
export function resizeAllCharts(): void {
|
||||
echartsResizeManager.resizeAllCharts()
|
||||
}
|
12
apps/web-antd/src/utils/flexible.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
function setRootFontSize(): void {
|
||||
const baseWidth = 1920 // 设计稿宽度
|
||||
const baseFontSize = 16 // 设计稿根字体
|
||||
const screenWidth = window.innerWidth
|
||||
const fontSize = (screenWidth / baseWidth) * baseFontSize
|
||||
document.documentElement.style.fontSize = fontSize + 'px'
|
||||
}
|
||||
|
||||
setRootFontSize()
|
||||
window.addEventListener('resize', setRootFontSize)
|
||||
|
||||
export default setRootFontSize
|
264
apps/web-antd/src/utils/pie3d.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl'
|
||||
|
||||
interface Pie3DData {
|
||||
name: string
|
||||
value: number
|
||||
itemStyle?: {
|
||||
color?: string
|
||||
opacity?: number
|
||||
}
|
||||
startRatio?: number
|
||||
endRatio?: number
|
||||
}
|
||||
|
||||
interface Pie3DOptions {
|
||||
data: Pie3DData[]
|
||||
height?: number
|
||||
hoverHeightScale?: number
|
||||
selectOffset?: number
|
||||
distance?: number
|
||||
boxHeight?: number
|
||||
radius?: number
|
||||
}
|
||||
|
||||
interface ParametricEquation {
|
||||
u: { min: number; max: number; step: number }
|
||||
v: { min: number; max: number; step: number }
|
||||
x: (u: number, v: number) => number
|
||||
y: (u: number, v: number) => number
|
||||
z: (u: number, v: number) => number
|
||||
}
|
||||
|
||||
interface SeriesItem {
|
||||
name: string
|
||||
type: string
|
||||
parametric: boolean
|
||||
wireframe: { show: boolean }
|
||||
pieData?: Pie3DData
|
||||
pieStatus?: { selected: boolean; hovered: boolean; k: number }
|
||||
itemStyle?: any
|
||||
parametricEquation?: ParametricEquation
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染3D饼图,支持点击选中(外移)、鼠标悬停高亮(升高)、高亮修正,无label/legend。
|
||||
* @param dom - 容器DOM
|
||||
* @param options - 配置项
|
||||
* options.data: [{name, value, itemStyle}]
|
||||
* options.height: 饼图厚度
|
||||
* options.hoverHeightScale: 高亮时高度放大倍数
|
||||
* options.selectOffset: 选中时外移距离(默认0.1)
|
||||
* options.distance: 视角距离
|
||||
* options.boxHeight: grid3D.boxHeight
|
||||
* options.radius: 半径缩放
|
||||
* @returns echartsInstance
|
||||
*/
|
||||
export function renderPie3DChart(dom: HTMLElement, options: Pie3DOptions): echarts.ECharts | undefined {
|
||||
if (!dom) return
|
||||
const myChart = echarts.init(dom)
|
||||
const hoverHeightScale = options.hoverHeightScale || 1.2
|
||||
const selectOffset = options.selectOffset || 0.1
|
||||
const distance = options.distance || 120
|
||||
const gridBoxHeight = options.boxHeight || 10
|
||||
const R = options.radius || 0.8
|
||||
const fixedH = 100
|
||||
|
||||
// 生成扇形的曲面参数方程
|
||||
function getParametricEquation(startRatio: number, endRatio: number, isSelected: boolean, isHovered: boolean, k: number, h: number): ParametricEquation {
|
||||
const midRatio = (startRatio + endRatio) / 2
|
||||
const startRadian = startRatio * Math.PI * 2
|
||||
const endRadian = endRatio * Math.PI * 2
|
||||
const midRadian = midRatio * Math.PI * 2
|
||||
if (startRatio === 0 && endRatio === 1) isSelected = false
|
||||
k = typeof k !== 'undefined' ? k : 1
|
||||
const offsetX = isSelected ? Math.cos(midRadian) * selectOffset : 0
|
||||
const offsetY = isSelected ? Math.sin(midRadian) * selectOffset : 0
|
||||
const hoverRate = isHovered ? 1.05 : 1
|
||||
return {
|
||||
u: { min: -Math.PI, max: Math.PI * 3, step: Math.PI / 32 },
|
||||
v: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
|
||||
x: function(u: number, v: number) {
|
||||
if (u < startRadian) return offsetX + R * Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
if (u > endRadian) return offsetX + R * Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
return offsetX + R * Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
|
||||
},
|
||||
y: function(u: number, v: number) {
|
||||
if (u < startRadian) return offsetY + R * Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
if (u > endRadian) return offsetY + R * Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
return offsetY + R * Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
|
||||
},
|
||||
z: function(u: number, v: number) {
|
||||
if (u < -Math.PI * 0.5) return Math.sin(u)
|
||||
if (u > Math.PI * 2.5) return Math.sin(u) * h * .1
|
||||
return Math.sin(v) > 0 ? 1 * h * .1 : -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成series
|
||||
function getPie3D(pieData: Pie3DData[]): SeriesItem[] {
|
||||
const series: SeriesItem[] = []
|
||||
let sumValue = 0
|
||||
let startValue = 0
|
||||
let endValue = 0
|
||||
const k = 1 // 实心饼图
|
||||
for (let i = 0; i < pieData.length; i++) {
|
||||
sumValue += pieData[i].value
|
||||
const seriesItem: SeriesItem = {
|
||||
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
|
||||
type: 'surface',
|
||||
parametric: true,
|
||||
wireframe: { show: false },
|
||||
pieData: pieData[i],
|
||||
pieStatus: { selected: false, hovered: false, k: k }
|
||||
}
|
||||
if (pieData[i].itemStyle) {
|
||||
const itemStyle: any = {}
|
||||
const itemStyleObj: any = pieData[i].itemStyle
|
||||
if (itemStyleObj.color) itemStyle.color = itemStyleObj.color
|
||||
if (itemStyleObj.opacity !== undefined) itemStyle.opacity = itemStyleObj.opacity
|
||||
seriesItem.itemStyle = itemStyle
|
||||
}
|
||||
series.push(seriesItem)
|
||||
}
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
endValue = startValue + series[i].pieData!.value
|
||||
series[i].pieData!.startRatio = startValue / sumValue
|
||||
series[i].pieData!.endRatio = endValue / sumValue
|
||||
series[i].parametricEquation = getParametricEquation(
|
||||
series[i].pieData!.startRatio!,
|
||||
series[i].pieData!.endRatio!,
|
||||
false,
|
||||
false,
|
||||
k,
|
||||
fixedH // 所有块厚度一样
|
||||
)
|
||||
startValue = endValue
|
||||
}
|
||||
// 透明圆环用于高亮修正
|
||||
series.push({
|
||||
name: 'mouseoutSeries',
|
||||
type: 'surface',
|
||||
parametric: true,
|
||||
wireframe: { show: false },
|
||||
itemStyle: { opacity: 0 },
|
||||
parametricEquation: {
|
||||
u: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
|
||||
v: { min: 0, max: Math.PI, step: Math.PI / 20 },
|
||||
x: function(u: number, v: number) { return Math.sin(v) * Math.sin(u) + Math.sin(u) },
|
||||
y: function(u: number, v: number) { return Math.sin(v) * Math.cos(u) + Math.cos(u) },
|
||||
z: function(u: number, v: number) { return Math.cos(v) > 0 ? 0.1 : -0.1 }
|
||||
}
|
||||
})
|
||||
return series
|
||||
}
|
||||
|
||||
const series = getPie3D(options.data)
|
||||
// 不加pie2d,不加legend,不加label
|
||||
const option = {
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
legend: undefined,
|
||||
tooltip: undefined,
|
||||
xAxis3D: { min: -1, max: 1 },
|
||||
yAxis3D: { min: -1, max: 1 },
|
||||
zAxis3D: { min: -1, max: 1 },
|
||||
grid3D: {
|
||||
show: false,
|
||||
boxHeight: gridBoxHeight,
|
||||
viewControl: {
|
||||
alpha: 35,
|
||||
distance: distance,
|
||||
rotateSensitivity: 0,
|
||||
zoomSensitivity: 0,
|
||||
panSensitivity: 0,
|
||||
autoRotate: false
|
||||
}
|
||||
},
|
||||
series: series
|
||||
}
|
||||
myChart.setOption(option, true)
|
||||
|
||||
// 选中/高亮交互
|
||||
let hoveredIndex = ''
|
||||
|
||||
// // 点击选中(外移)
|
||||
// myChart.on('click', function(params) {
|
||||
// if (!series[params.seriesIndex] || series[params.seriesIndex].type !== 'surface') return;
|
||||
// let isSelected = !series[params.seriesIndex].pieStatus.selected;
|
||||
// let isHovered = series[params.seriesIndex].pieStatus.hovered;
|
||||
// let k = series[params.seriesIndex].pieStatus.k;
|
||||
// let startRatio = series[params.seriesIndex].pieData.startRatio;
|
||||
// let endRatio = series[params.seriesIndex].pieData.endRatio;
|
||||
// // 取消之前选中
|
||||
// if (selectedIndex !== '' && selectedIndex !== params.seriesIndex) {
|
||||
// let prev = series[selectedIndex];
|
||||
// prev.parametricEquation = getParametricEquation(
|
||||
// prev.pieData.startRatio, prev.pieData.endRatio, false, prev.pieStatus.hovered, k, prev.pieData.value
|
||||
// );
|
||||
// prev.pieStatus.selected = false;
|
||||
// }
|
||||
// // 当前选中/取消
|
||||
// series[params.seriesIndex].parametricEquation = getParametricEquation(
|
||||
// startRatio, endRatio, isSelected, isHovered, k, series[params.seriesIndex].pieData.value
|
||||
// );
|
||||
// series[params.seriesIndex].pieStatus.selected = isSelected;
|
||||
// isSelected ? selectedIndex = params.seriesIndex : selectedIndex = '';
|
||||
// myChart.setOption({ series });
|
||||
// });
|
||||
|
||||
// 悬停高亮(升高)
|
||||
myChart.on('mouseover', function(params: any) {
|
||||
if (!series[params.seriesIndex] || !series[params.seriesIndex].pieData) return
|
||||
if (hoveredIndex === params.seriesIndex) return
|
||||
// 取消之前高亮
|
||||
if (hoveredIndex !== '') {
|
||||
const prev = series[parseInt(hoveredIndex)]
|
||||
if (!prev || !prev.pieData) return
|
||||
prev.parametricEquation = getParametricEquation(
|
||||
prev.pieData.startRatio!,
|
||||
prev.pieData.endRatio!,
|
||||
prev.pieStatus!.selected,
|
||||
false,
|
||||
prev.pieStatus!.k,
|
||||
fixedH
|
||||
)
|
||||
prev.pieStatus!.hovered = false
|
||||
hoveredIndex = ''
|
||||
}
|
||||
// 当前高亮
|
||||
const cur = series[params.seriesIndex]
|
||||
cur.parametricEquation = getParametricEquation(
|
||||
cur.pieData!.startRatio!,
|
||||
cur.pieData!.endRatio!,
|
||||
cur.pieStatus!.selected,
|
||||
true,
|
||||
cur.pieStatus!.k,
|
||||
fixedH * hoverHeightScale
|
||||
)
|
||||
cur.pieStatus!.hovered = true
|
||||
hoveredIndex = params.seriesIndex.toString()
|
||||
myChart.setOption({ series })
|
||||
})
|
||||
|
||||
// 全局移出,修正高亮残留
|
||||
myChart.on('globalout', function() {
|
||||
if (hoveredIndex !== '') {
|
||||
const prev = series[parseInt(hoveredIndex)]
|
||||
if (!prev || !prev.pieData) return
|
||||
prev.parametricEquation = getParametricEquation(
|
||||
prev.pieData.startRatio!,
|
||||
prev.pieData.endRatio!,
|
||||
prev.pieStatus!.selected,
|
||||
false,
|
||||
prev.pieStatus!.k,
|
||||
fixedH
|
||||
)
|
||||
prev.pieStatus!.hovered = false
|
||||
hoveredIndex = ''
|
||||
myChart.setOption({ series })
|
||||
}
|
||||
})
|
||||
|
||||
return myChart
|
||||
}
|
129
apps/web-antd/src/utils/threeDBarOption.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
interface ThreeDBarOptionConfig {
|
||||
xData: string[]
|
||||
yData: number[]
|
||||
barWidth?: number
|
||||
colorConfig?: {
|
||||
left0?: string
|
||||
left08?: string
|
||||
top03?: string
|
||||
top1?: string
|
||||
}
|
||||
}
|
||||
|
||||
export function getThreeDBarOption({ xData, yData, barWidth = 30, colorConfig = {} }: ThreeDBarOptionConfig) {
|
||||
// 注册 shape 只需一次,防止多次注册报错
|
||||
if (!(echarts.graphic as any).registered3DBar) {
|
||||
const leftShape = echarts.graphic.extendShape({
|
||||
buildPath(ctx: any, shape: any) {
|
||||
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
|
||||
const WIDTH = 15
|
||||
const OBLIQUE_ANGLE_HEIGHT = 8
|
||||
const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
const p2 = [basicsXAxis - WIDTH, bottomYAxis]
|
||||
const p3 = [basicsXAxis, bottomYAxis]
|
||||
const p4 = [basicsXAxis, topBasicsYAxis]
|
||||
ctx.moveTo(p1[0], p1[1])
|
||||
ctx.lineTo(p2[0], p2[1])
|
||||
ctx.lineTo(p3[0], p3[1])
|
||||
ctx.lineTo(p4[0], p4[1])
|
||||
}
|
||||
})
|
||||
const rightShape = echarts.graphic.extendShape({
|
||||
buildPath(ctx: any, shape: any) {
|
||||
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
|
||||
const WIDTH = 15
|
||||
const OBLIQUE_ANGLE_HEIGHT = 2
|
||||
const p1 = [basicsXAxis, topBasicsYAxis]
|
||||
const p2 = [basicsXAxis, bottomYAxis]
|
||||
const p3 = [basicsXAxis + WIDTH, bottomYAxis]
|
||||
const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
ctx.moveTo(p1[0], p1[1])
|
||||
ctx.lineTo(p2[0], p2[1])
|
||||
ctx.lineTo(p3[0], p3[1])
|
||||
ctx.lineTo(p4[0], p4[1])
|
||||
}
|
||||
})
|
||||
const topShape = echarts.graphic.extendShape({
|
||||
buildPath(ctx: any, shape: any) {
|
||||
const { topBasicsYAxis, basicsXAxis } = shape
|
||||
const WIDTH = 15
|
||||
const OBLIQUE_ANGLE_HEIGHT = 8
|
||||
const p1 = [basicsXAxis, topBasicsYAxis]
|
||||
const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2]
|
||||
const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
ctx.moveTo(p1[0], p1[1])
|
||||
ctx.lineTo(p2[0], p2[1])
|
||||
ctx.lineTo(p3[0], p3[1])
|
||||
ctx.lineTo(p4[0], p4[1])
|
||||
}
|
||||
})
|
||||
echarts.graphic.registerShape('leftShape', leftShape)
|
||||
echarts.graphic.registerShape('rightShape', rightShape)
|
||||
echarts.graphic.registerShape('topShape', topShape)
|
||||
;(echarts.graphic as any).registered3DBar = true
|
||||
}
|
||||
|
||||
return {
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', top: "12%", containLabel: true },
|
||||
xAxis: [{
|
||||
type: 'category',
|
||||
data: xData,
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
}],
|
||||
yAxis: [{
|
||||
type: 'value',
|
||||
axisLine: { show: false },
|
||||
splitLine: { lineStyle: { color: '#1b4a7a' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
}],
|
||||
series: [{
|
||||
type: 'custom',
|
||||
data: yData,
|
||||
barWidth,
|
||||
renderItem(params: any, api: any) {
|
||||
const basicsCoord = api.coord([api.value(0), api.value(1)])
|
||||
const topBasicsYAxis = basicsCoord[1]
|
||||
const basicsXAxis = basicsCoord[0]
|
||||
const bottomYAxis = api.coord([api.value(0), 0])[1]
|
||||
return {
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
type: 'leftShape',
|
||||
shape: { topBasicsYAxis, basicsXAxis, bottomYAxis },
|
||||
style: {
|
||||
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: colorConfig.left0 || '#2ADBEF' },
|
||||
{ offset: 1, color: colorConfig.left08 || '#2677F9 ' },
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'rightShape',
|
||||
shape: { topBasicsYAxis, basicsXAxis, bottomYAxis },
|
||||
style: {
|
||||
fill: '#114EAD '
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'topShape',
|
||||
shape: { topBasicsYAxis, basicsXAxis, bottomYAxis },
|
||||
style: {
|
||||
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0.3, color: colorConfig.top03 || '#6DF0FF' },
|
||||
{ offset: 1, color: colorConfig.top1 || '#6DF0FF' }
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
@@ -41,6 +41,7 @@ const isUpdate = ref(false);
|
||||
const isReadonly = ref(false);
|
||||
const isAudit = ref(false);
|
||||
const isRefund = ref(false);
|
||||
const rowData = ref<any>({});
|
||||
const title = computed(() => {
|
||||
return isUpdate.value ? $t('pages.common.edit') : isReadonly.value ? '详情' : $t('pages.common.add');
|
||||
});
|
||||
@@ -123,16 +124,18 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
// 查询服务地址树形结构
|
||||
setupCommunitySelect()
|
||||
modalApi.modalLoading(true);
|
||||
const { id, readonly,audit,refund } = modalApi.getData() as {
|
||||
const { id, readonly,audit,refund,row } = modalApi.getData() as {
|
||||
id?: string;
|
||||
readonly?: boolean;
|
||||
audit?: boolean;
|
||||
refund?: boolean;
|
||||
row?: any;
|
||||
};
|
||||
editCleanOrderId.value = id || '';
|
||||
isReadonly.value = !!readonly;
|
||||
isAudit.value = !!audit;
|
||||
isRefund.value = !!refund;
|
||||
rowData.value = row;
|
||||
//判断是否是编辑状态需要先判断是否是只读状态
|
||||
if(isReadonly.value){
|
||||
isUpdate.value = false;
|
||||
@@ -400,6 +403,10 @@ async function handleAudit(params: any) {
|
||||
return;
|
||||
}
|
||||
const data = cloneDeep(await formApi.getValues());
|
||||
if(rowData.value){
|
||||
data.state = rowData.value.state;
|
||||
data.isUnbooking = rowData.value?.isUnbooking;
|
||||
}
|
||||
// 单位数据缓存
|
||||
if (unitListData.length === 0) {
|
||||
const res = await resident_unitList();
|
||||
@@ -426,7 +433,6 @@ async function handleAudit(params: any) {
|
||||
}else{
|
||||
data.state = 2;
|
||||
}
|
||||
console.log(data,'data');
|
||||
// 0:未审核 1:审核通过 2:审核不通过
|
||||
await clean_orderUpdate({...data,id:editCleanOrderId.value})
|
||||
resetInitialized();
|
||||
|
@@ -83,24 +83,24 @@ export const columns: VxeGridProps['columns'] = [
|
||||
field: 'payState',
|
||||
width: '120',
|
||||
slots: {
|
||||
default: ({ row }) => (row.state === 1 ? '已支付' : '待支付'),
|
||||
default: ({ row }) => (row.payState === 1 ? '已支付' : '待支付'),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '服务开始时间',
|
||||
field: 'starTime',
|
||||
width: 'auto',
|
||||
width: '200',
|
||||
},
|
||||
{
|
||||
title: '服务结束时间',
|
||||
field: 'endTime',
|
||||
width: 'auto',
|
||||
},
|
||||
{
|
||||
title: '申请单位',
|
||||
field: 'unit',
|
||||
width: '150',
|
||||
width: '200',
|
||||
},
|
||||
// {
|
||||
// title: '申请单位',
|
||||
// field: 'unit',
|
||||
// width: '150',
|
||||
// },
|
||||
{
|
||||
title: '申请人',
|
||||
field: 'persion',
|
||||
@@ -109,7 +109,7 @@ export const columns: VxeGridProps['columns'] = [
|
||||
{
|
||||
title: '联系电话',
|
||||
field: 'phone',
|
||||
width: 'auto',
|
||||
width: '250',
|
||||
},
|
||||
{
|
||||
title: '订单状态',
|
||||
@@ -126,6 +126,7 @@ export const columns: VxeGridProps['columns'] = [
|
||||
fixed: 'right',
|
||||
slots: { default: 'action' },
|
||||
title: '操作',
|
||||
width:'200'
|
||||
},
|
||||
|
||||
];
|
||||
|
@@ -136,7 +136,7 @@ function handleAudit(row:any) {
|
||||
// 审核逻辑
|
||||
// TODO: 实现审核功能
|
||||
console.log('审核', row);
|
||||
modalApi.setData({ id: row.id, readonly: true,audit:true });
|
||||
modalApi.setData({ id: row.id, readonly: true,audit:true,row:row });
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ function handleRefund(row:any) {
|
||||
// 退定逻辑
|
||||
// TODO: 实现退定功能
|
||||
console.log('退定', row);
|
||||
modalApi.setData({ id: row.id, readonly: true,refund:true });
|
||||
modalApi.setData({ id: row.id, readonly: true,refund:true,row:row });
|
||||
modalApi.open();
|
||||
}
|
||||
</script>
|
||||
|
@@ -103,14 +103,14 @@ const detailSchema = [
|
||||
options: getDictOptions('pro_product_classification'),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '图片',
|
||||
fieldName: 'imgPath',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '图片',
|
||||
// fieldName: 'imgPath',
|
||||
// component: 'InputNumber',
|
||||
// componentProps: {
|
||||
// disabled: true,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
label: '规格',
|
||||
fieldName: 'specification',
|
||||
|
@@ -12,6 +12,7 @@ import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||
import { modalSchema } from './data';
|
||||
import productDetailModal from './rentalPlan-detial-modal.vue';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
import { Item } from 'ant-design-vue/es/menu';
|
||||
|
||||
const emit = defineEmits<{ reload: [] }>();
|
||||
|
||||
@@ -69,7 +70,10 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
if ((isUpdate.value || isReadonly.value) && id) {
|
||||
const record = await rentalPlanInfo(id);
|
||||
// 后端返回绿植产品包列表结构处理
|
||||
detailTable.value = record.productList.map((item:any) => item.product);
|
||||
// detailTable.value = record.productList.map((item:any) => {...item.product,item.productNum,});
|
||||
detailTable.value = record.productList.map((item: any) => ({ ...item.product, productNum: item.productNum,productId: item.productId }));
|
||||
// console.log(detailTable.value);
|
||||
|
||||
await formApi.setValues(record);
|
||||
}
|
||||
await markInitialized();
|
||||
@@ -116,11 +120,11 @@ const detailColumns = [
|
||||
{ title: '产品名称', dataIndex: 'plantName', key: 'plantName' },
|
||||
{ title: '产品分类', dataIndex: 'plantType', key: 'plantType' },
|
||||
{ title: '产品数量', dataIndex: 'productNum', key: 'productNum' },
|
||||
{
|
||||
title: '图片',
|
||||
dataIndex: 'imgPath',
|
||||
key: 'imgPath',
|
||||
},
|
||||
// {
|
||||
// title: '图片',
|
||||
// dataIndex: 'imgPath',
|
||||
// key: 'imgPath',
|
||||
// },
|
||||
{
|
||||
title: '规格',
|
||||
dataIndex: 'specification',
|
||||
|
@@ -87,10 +87,14 @@ onMounted(async () => {
|
||||
data: xAxisData.value,
|
||||
boundaryGap: false,
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
yAxis: { type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (value) => `${value * 100}%`
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '订单数',
|
||||
name: '订单趋势',
|
||||
type: 'line',
|
||||
data: seriesData.value ||[],
|
||||
smooth: true,
|
||||
@@ -123,17 +127,22 @@ onMounted(async () => {
|
||||
],
|
||||
});
|
||||
renderCustomerTypesBar({
|
||||
title: { text: '客户类型分配' },
|
||||
title: { text: '客户类型分布' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: countByCusTypeData.type || [],
|
||||
data: ['企业客户','个人客户','政府机构','商业地产','其他'],
|
||||
boundaryGap: true,
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
yAxis: { type: 'value',
|
||||
axisLabel: {
|
||||
// formatter: (value: number) => `${parseInt(value.toString())}`
|
||||
},
|
||||
// interval: 0,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '订单数',
|
||||
name: '客户数',
|
||||
type: 'bar',
|
||||
data: countByCusTypeData.counts || [],
|
||||
},
|
||||
@@ -141,16 +150,28 @@ onMounted(async () => {
|
||||
});
|
||||
renderCustomerRenewalLine({
|
||||
title: { text: '客户续租率趋势' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
tooltip: { trigger: 'axis',
|
||||
formatter: function(params:any) {
|
||||
let result = params[0].axisValue + '<br/>';
|
||||
params.forEach((item:any) => {
|
||||
result += item.marker + item.seriesName + ':' + item.data + '%<br/>';
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: countRenewRateData.month || [],
|
||||
boundaryGap: false,
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: '{value}%',
|
||||
}, },
|
||||
series: [
|
||||
{
|
||||
name: '订单数',
|
||||
name: '续租率',
|
||||
type: 'line',
|
||||
data: countRenewRateData.rate || [],
|
||||
smooth: true,
|
||||
@@ -162,6 +183,19 @@ onMounted(async () => {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
formatter: function(params:any) {
|
||||
// params 是一个数组,包含每个系列的当前项
|
||||
let result = params[0].axisValue + '<br/>';
|
||||
params.forEach((item:any) => {
|
||||
if (item.seriesName === '完成率') {
|
||||
// 假设原始数据是 80,显示为 80%
|
||||
result += item.marker + item.seriesName + ':' + item.data + '%<br/>';
|
||||
} else {
|
||||
result += item.marker + item.seriesName + ':' + item.data + '<br/>';
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['计划任务数', '已完成数', '完成率'],
|
||||
@@ -169,7 +203,7 @@ onMounted(async () => {
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: countAchievedData.type || [],
|
||||
data: ['修剪整形','肥水管理','中耕除草','病虫害防治','越冬防寒'],
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
@@ -195,7 +229,7 @@ onMounted(async () => {
|
||||
{
|
||||
name: '计划任务数',
|
||||
type: 'bar',
|
||||
data: countAchievedData.toral || [],
|
||||
data: countAchievedData.total || [],
|
||||
},
|
||||
{
|
||||
name: '已完成数',
|
||||
@@ -220,7 +254,7 @@ onMounted(async () => {
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
bottom: 10,
|
||||
data: ['一星', '两星', '三星', '四星', '五星'],
|
||||
data: ['一星', '二星', '三星', '四星', '五星'],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@@ -283,7 +317,7 @@ function formatNumber(num: number | string) {
|
||||
<div class="title">
|
||||
<div class="title-text">绿植租赁业务统计报表:</div>
|
||||
<div class="title-operate">
|
||||
<div class="export">
|
||||
<div class="export" style="display: none;">
|
||||
<Button size="large" style="color: #fff; background-color: #22c55e">
|
||||
导出数据
|
||||
</Button>
|
||||
|
@@ -0,0 +1,220 @@
|
||||
import type {FormSchemaGetter} from '#/adapter/form';
|
||||
import type {VxeGridProps} from '#/adapter/vxe-table';
|
||||
import {getDictOptions} from "#/utils/dict";
|
||||
import {renderDict} from "#/utils/render";
|
||||
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'planName',
|
||||
label: '计划名称',
|
||||
},
|
||||
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions('wy_xjzq'),
|
||||
},
|
||||
fieldName: 'inspectionPlanPeriod',
|
||||
label: '巡检周期',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions('wy_xjqdfs'),
|
||||
},
|
||||
fieldName: 'signType',
|
||||
label: '签到方式',
|
||||
},
|
||||
];
|
||||
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
{type: 'checkbox', width: 60},
|
||||
{
|
||||
title: '主键id',
|
||||
field: 'id',
|
||||
},
|
||||
{
|
||||
title: '巡检计划名称',
|
||||
field: 'planName',
|
||||
},
|
||||
{
|
||||
title: '巡检路线',
|
||||
field: 'inspectionRouteId',
|
||||
},
|
||||
{
|
||||
title: '巡检周期',
|
||||
field: 'inspectionPlanPeriod',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.inspectionPlanPeriod, 'wy_xjzq');
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '任务提前分组',
|
||||
field: 'beforeTime',
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
field: 'startDate',
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
field: 'endDate',
|
||||
},
|
||||
{
|
||||
title: '开始时间',
|
||||
field: 'startTime',
|
||||
},
|
||||
{
|
||||
title: '结束时间',
|
||||
field: 'endTime',
|
||||
},
|
||||
{
|
||||
title: '签到方式',
|
||||
field: 'signType',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.signType, 'wy_xjqdfs');
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '是否允许补检',
|
||||
field: 'canReexamine',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.canReexamine, 'wy_sf');
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '选择员工',
|
||||
field: 'userId',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
field: 'remark',
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
slots: {default: 'action'},
|
||||
title: '操作',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
export const modalSchema: FormSchemaGetter = () => [
|
||||
{
|
||||
label: '主键id',
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
show: () => false,
|
||||
triggerFields: [''],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '巡检计划名称',
|
||||
fieldName: 'planName',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '巡检路线',
|
||||
fieldName: 'inspectionRouteId',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '巡检周期',
|
||||
fieldName: 'inspectionPlanPeriod',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
componentProps: {
|
||||
options: getDictOptions('wy_xjzq'),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '任务提前分组',
|
||||
fieldName: 'beforeTime',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '开始日期',
|
||||
fieldName: 'startDate',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '结束日期',
|
||||
fieldName: 'endDate',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '开始时间',
|
||||
fieldName: 'startTime',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '结束时间',
|
||||
fieldName: 'endTime',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '签到方式',
|
||||
fieldName: 'signType',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions('wy_xjqdfs'),
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
label: '允许补检',
|
||||
fieldName: 'canReexamine',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions('wy_sf'),
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
label: '选择员工',
|
||||
fieldName: 'userId',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
fieldName: 'remark',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
@@ -1,24 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { DevicePointForm } from '#/api/sis/devicePoint/model';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
|
||||
import {
|
||||
devicePointExport,
|
||||
devicePointList,
|
||||
devicePointRemove,
|
||||
} from '#/api/sis/devicePoint';
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps
|
||||
} from '#/adapter/vxe-table';
|
||||
|
||||
import {
|
||||
inspectionPlanExport,
|
||||
inspectionPlanList,
|
||||
inspectionPlanRemove,
|
||||
} from '#/api/property/inspectionManagement/inspectionPlan';
|
||||
import type { InspectionPlanForm } from '#/api/property/inspectionManagement/inspectionPlan/model';
|
||||
import { commonDownloadExcel } from '#/utils/file/download';
|
||||
|
||||
import inspectionPlanModal from './inspectionPlan-modal.vue';
|
||||
import { columns, querySchema } from './data';
|
||||
import devicePointModal from './devicePoint-modal.vue';
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
commonConfig: {
|
||||
@@ -58,7 +64,7 @@ const gridOptions: VxeGridProps = {
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues = {}) => {
|
||||
return await devicePointList({
|
||||
return await inspectionPlanList({
|
||||
pageNum: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
@@ -70,7 +76,7 @@ const gridOptions: VxeGridProps = {
|
||||
keyField: 'id',
|
||||
},
|
||||
// 表格全局唯一表示 保存列配置需要用到
|
||||
id: 'sis-devicePoint-index',
|
||||
id: 'property-inspectionPlan-index'
|
||||
};
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
@@ -78,8 +84,8 @@ const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
gridOptions,
|
||||
});
|
||||
|
||||
const [DevicePointModal, modalApi] = useVbenModal({
|
||||
connectedComponent: devicePointModal,
|
||||
const [InspectionPlanModal, modalApi] = useVbenModal({
|
||||
connectedComponent: inspectionPlanModal,
|
||||
});
|
||||
|
||||
function handleAdd() {
|
||||
@@ -87,49 +93,44 @@ function handleAdd() {
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
async function handleEdit(row: Required<DevicePointForm>) {
|
||||
async function handleEdit(row: Required<InspectionPlanForm>) {
|
||||
modalApi.setData({ id: row.id });
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
async function handleDelete(row: Required<DevicePointForm>) {
|
||||
await devicePointRemove(row.id);
|
||||
async function handleDelete(row: Required<InspectionPlanForm>) {
|
||||
await inspectionPlanRemove(row.id);
|
||||
await tableApi.query();
|
||||
}
|
||||
|
||||
function handleMultiDelete() {
|
||||
const rows = tableApi.grid.getCheckboxRecords();
|
||||
const ids = rows.map((row: Required<DevicePointForm>) => row.id);
|
||||
const ids = rows.map((row: Required<InspectionPlanForm>) => row.id);
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
okType: 'danger',
|
||||
content: `确认删除选中的${ids.length}条记录吗?`,
|
||||
onOk: async () => {
|
||||
await devicePointRemove(ids);
|
||||
await inspectionPlanRemove(ids);
|
||||
await tableApi.query();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleDownloadExcel() {
|
||||
commonDownloadExcel(
|
||||
devicePointExport,
|
||||
'宇视设备点位(通道)数据',
|
||||
tableApi.formApi.form.values,
|
||||
{
|
||||
fieldMappingTime: formOptions.fieldMappingTime,
|
||||
},
|
||||
);
|
||||
commonDownloadExcel(inspectionPlanExport, '巡检计划数据', tableApi.formApi.form.values, {
|
||||
fieldMappingTime: formOptions.fieldMappingTime,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable table-title="宇视设备点位(通道)列表">
|
||||
<BasicTable table-title="巡检计划列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
v-access:code="['sis:devicePoint:export']"
|
||||
v-access:code="['property:inspectionPlan:export']"
|
||||
@click="handleDownloadExcel"
|
||||
>
|
||||
{{ $t('pages.common.export') }}
|
||||
@@ -138,14 +139,13 @@ function handleDownloadExcel() {
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['sis:devicePoint:remove']"
|
||||
@click="handleMultiDelete"
|
||||
>
|
||||
v-access:code="['property:inspectionPlan:remove']"
|
||||
@click="handleMultiDelete">
|
||||
{{ $t('pages.common.delete') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-access:code="['sis:devicePoint:add']"
|
||||
v-access:code="['property:inspectionPlan:add']"
|
||||
@click="handleAdd"
|
||||
>
|
||||
{{ $t('pages.common.add') }}
|
||||
@@ -155,7 +155,7 @@ function handleDownloadExcel() {
|
||||
<template #action="{ row }">
|
||||
<Space>
|
||||
<ghost-button
|
||||
v-access:code="['sis:devicePoint:edit']"
|
||||
v-access:code="['property:inspectionPlan:edit']"
|
||||
@click.stop="handleEdit(row)"
|
||||
>
|
||||
{{ $t('pages.common.edit') }}
|
||||
@@ -168,7 +168,7 @@ function handleDownloadExcel() {
|
||||
>
|
||||
<ghost-button
|
||||
danger
|
||||
v-access:code="['sis:devicePoint:remove']"
|
||||
v-access:code="['property:inspectionPlan:remove']"
|
||||
@click.stop=""
|
||||
>
|
||||
{{ $t('pages.common.delete') }}
|
||||
@@ -177,6 +177,6 @@ function handleDownloadExcel() {
|
||||
</Space>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<DevicePointModal @reload="tableApi.query()" />
|
||||
<InspectionPlanModal @reload="tableApi.query()" />
|
||||
</Page>
|
||||
</template>
|
@@ -6,11 +6,7 @@ import { $t } from '@vben/locales';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
devicePointAdd,
|
||||
devicePointInfo,
|
||||
devicePointUpdate,
|
||||
} from '#/api/sis/devicePoint';
|
||||
import { inspectionPlanAdd, inspectionPlanInfo, inspectionPlanUpdate } from '#/api/property/inspectionManagement/inspectionPlan';
|
||||
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||
|
||||
import { modalSchema } from './data';
|
||||
@@ -31,7 +27,7 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
// 通用配置项 会影响到所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
}
|
||||
},
|
||||
schema: modalSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -62,7 +58,7 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
isUpdate.value = !!id;
|
||||
|
||||
if (isUpdate.value && id) {
|
||||
const record = await devicePointInfo(id);
|
||||
const record = await inspectionPlanInfo(id);
|
||||
await formApi.setValues(record);
|
||||
}
|
||||
await markInitialized();
|
||||
@@ -80,7 +76,7 @@ async function handleConfirm() {
|
||||
}
|
||||
// getValues获取为一个readonly的对象 需要修改必须先深拷贝一次
|
||||
const data = cloneDeep(await formApi.getValues());
|
||||
await (isUpdate.value ? devicePointUpdate(data) : devicePointAdd(data));
|
||||
await (isUpdate.value ? inspectionPlanUpdate(data) : inspectionPlanAdd(data));
|
||||
resetInitialized();
|
||||
emit('reload');
|
||||
modalApi.close();
|
||||
@@ -102,3 +98,4 @@ async function handleClosed() {
|
||||
<BasicForm />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
@@ -0,0 +1,59 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'routeName',
|
||||
label: '路线名称',
|
||||
},
|
||||
];
|
||||
|
||||
// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新
|
||||
// export const columns: () => VxeGridProps['columns'] = () => [
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{
|
||||
title: '主键id',
|
||||
field: 'id',
|
||||
},
|
||||
{
|
||||
title: '路线名称',
|
||||
field: 'routeName',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
field: 'remark',
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
slots: { default: 'action' },
|
||||
title: '操作',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
export const modalSchema: FormSchemaGetter = () => [
|
||||
{
|
||||
label: '主键id',
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
show: () => false,
|
||||
triggerFields: [''],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '路线名称',
|
||||
fieldName: 'routeName',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
fieldName: 'remark',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
@@ -1,23 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { AccessControlDeviceForm } from '#/api/sis/accessControlDevice/model';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
|
||||
import {
|
||||
accessControlDeviceExport,
|
||||
accessControlDeviceList,
|
||||
accessControlDeviceRemove,
|
||||
} from '#/api/sis/accessControlDevice';
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps
|
||||
} from '#/adapter/vxe-table';
|
||||
|
||||
import {
|
||||
inspectionRouteExport,
|
||||
inspectionRouteList,
|
||||
inspectionRouteRemove,
|
||||
} from '#/api/property/inspectionManagement/inspectionRoute';
|
||||
import type { InspectionRouteForm } from '#/api/property/inspectionManagement/inspectionRoute/model';
|
||||
import { commonDownloadExcel } from '#/utils/file/download';
|
||||
|
||||
import accessControlDeviceModal from './accessControlDevice-modal.vue';
|
||||
import inspectionRouteModal from './inspectionRoute-modal.vue';
|
||||
import { columns, querySchema } from './data';
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
@@ -58,7 +64,7 @@ const gridOptions: VxeGridProps = {
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues = {}) => {
|
||||
return await accessControlDeviceList({
|
||||
return await inspectionRouteList({
|
||||
pageNum: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
@@ -70,7 +76,7 @@ const gridOptions: VxeGridProps = {
|
||||
keyField: 'id',
|
||||
},
|
||||
// 表格全局唯一表示 保存列配置需要用到
|
||||
id: 'sis-accessControlDevice-index',
|
||||
id: 'property-inspectionRoute-index'
|
||||
};
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
@@ -78,8 +84,8 @@ const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
gridOptions,
|
||||
});
|
||||
|
||||
const [AccessControlDeviceModal, modalApi] = useVbenModal({
|
||||
connectedComponent: accessControlDeviceModal,
|
||||
const [InspectionRouteModal, modalApi] = useVbenModal({
|
||||
connectedComponent: inspectionRouteModal,
|
||||
});
|
||||
|
||||
function handleAdd() {
|
||||
@@ -87,49 +93,44 @@ function handleAdd() {
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
async function handleEdit(row: Required<AccessControlDeviceForm>) {
|
||||
async function handleEdit(row: Required<InspectionRouteForm>) {
|
||||
modalApi.setData({ id: row.id });
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
async function handleDelete(row: Required<AccessControlDeviceForm>) {
|
||||
await accessControlDeviceRemove(row.id);
|
||||
async function handleDelete(row: Required<InspectionRouteForm>) {
|
||||
await inspectionRouteRemove(row.id);
|
||||
await tableApi.query();
|
||||
}
|
||||
|
||||
function handleMultiDelete() {
|
||||
const rows = tableApi.grid.getCheckboxRecords();
|
||||
const ids = rows.map((row: Required<AccessControlDeviceForm>) => row.id);
|
||||
const ids = rows.map((row: Required<InspectionRouteForm>) => row.id);
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
okType: 'danger',
|
||||
content: `确认删除选中的${ids.length}条记录吗?`,
|
||||
onOk: async () => {
|
||||
await accessControlDeviceRemove(ids);
|
||||
await inspectionRouteRemove(ids);
|
||||
await tableApi.query();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleDownloadExcel() {
|
||||
commonDownloadExcel(
|
||||
accessControlDeviceExport,
|
||||
'门禁设备数据',
|
||||
tableApi.formApi.form.values,
|
||||
{
|
||||
fieldMappingTime: formOptions.fieldMappingTime,
|
||||
},
|
||||
);
|
||||
commonDownloadExcel(inspectionRouteExport, '巡检路线数据', tableApi.formApi.form.values, {
|
||||
fieldMappingTime: formOptions.fieldMappingTime,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable table-title="门禁设备列表">
|
||||
<BasicTable table-title="巡检路线列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
v-access:code="['sis:accessControlDevice:export']"
|
||||
v-access:code="['property:inspectionRoute:export']"
|
||||
@click="handleDownloadExcel"
|
||||
>
|
||||
{{ $t('pages.common.export') }}
|
||||
@@ -138,14 +139,13 @@ function handleDownloadExcel() {
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['sis:accessControlDevice:remove']"
|
||||
@click="handleMultiDelete"
|
||||
>
|
||||
v-access:code="['property:inspectionRoute:remove']"
|
||||
@click="handleMultiDelete">
|
||||
{{ $t('pages.common.delete') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-access:code="['sis:accessControlDevice:add']"
|
||||
v-access:code="['property:inspectionRoute:add']"
|
||||
@click="handleAdd"
|
||||
>
|
||||
{{ $t('pages.common.add') }}
|
||||
@@ -155,7 +155,7 @@ function handleDownloadExcel() {
|
||||
<template #action="{ row }">
|
||||
<Space>
|
||||
<ghost-button
|
||||
v-access:code="['sis:accessControlDevice:edit']"
|
||||
v-access:code="['property:inspectionRoute:edit']"
|
||||
@click.stop="handleEdit(row)"
|
||||
>
|
||||
{{ $t('pages.common.edit') }}
|
||||
@@ -168,7 +168,7 @@ function handleDownloadExcel() {
|
||||
>
|
||||
<ghost-button
|
||||
danger
|
||||
v-access:code="['sis:accessControlDevice:remove']"
|
||||
v-access:code="['property:inspectionRoute:remove']"
|
||||
@click.stop=""
|
||||
>
|
||||
{{ $t('pages.common.delete') }}
|
||||
@@ -177,6 +177,6 @@ function handleDownloadExcel() {
|
||||
</Space>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<AccessControlDeviceModal @reload="tableApi.query()" />
|
||||
<InspectionRouteModal @reload="tableApi.query()" />
|
||||
</Page>
|
||||
</template>
|
@@ -6,11 +6,7 @@ import { $t } from '@vben/locales';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
accessControlDeviceAdd,
|
||||
accessControlDeviceInfo,
|
||||
accessControlDeviceUpdate,
|
||||
} from '#/api/sis/accessControlDevice';
|
||||
import { inspectionRouteAdd, inspectionRouteInfo, inspectionRouteUpdate } from '#/api/property/inspectionManagement/inspectionRoute';
|
||||
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||
|
||||
import { modalSchema } from './data';
|
||||
@@ -25,13 +21,13 @@ const title = computed(() => {
|
||||
const [BasicForm, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
// 默认占满两列
|
||||
formItemClass: 'col-span-1',
|
||||
formItemClass: 'col-span-2',
|
||||
// 默认label宽度 px
|
||||
labelWidth: 80,
|
||||
// 通用配置项 会影响到所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
}
|
||||
},
|
||||
schema: modalSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -47,7 +43,7 @@ const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||
|
||||
const [BasicModal, modalApi] = useVbenModal({
|
||||
// 在这里更改宽度
|
||||
class: 'w-[60%]',
|
||||
class: 'w-[550px]',
|
||||
fullscreenButton: false,
|
||||
onBeforeClose,
|
||||
onClosed: handleClosed,
|
||||
@@ -62,7 +58,7 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
isUpdate.value = !!id;
|
||||
|
||||
if (isUpdate.value && id) {
|
||||
const record = await accessControlDeviceInfo(id);
|
||||
const record = await inspectionRouteInfo(id);
|
||||
await formApi.setValues(record);
|
||||
}
|
||||
await markInitialized();
|
||||
@@ -80,9 +76,7 @@ async function handleConfirm() {
|
||||
}
|
||||
// getValues获取为一个readonly的对象 需要修改必须先深拷贝一次
|
||||
const data = cloneDeep(await formApi.getValues());
|
||||
await (isUpdate.value
|
||||
? accessControlDeviceUpdate(data)
|
||||
: accessControlDeviceAdd(data));
|
||||
await (isUpdate.value ? inspectionRouteUpdate(data) : inspectionRouteAdd(data));
|
||||
resetInitialized();
|
||||
emit('reload');
|
||||
modalApi.close();
|
||||
@@ -104,3 +98,4 @@ async function handleClosed() {
|
||||
<BasicForm />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
@@ -60,7 +60,8 @@ function generateWeekDates(): void {
|
||||
return startOfWeek.add(i, 'day').format('YYYY-MM-DD');
|
||||
});
|
||||
weekDates.value = dates;
|
||||
selectedDate.value = dates[0] ?? '';
|
||||
// 默认选中今天
|
||||
selectedDate.value = today.format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
// 获取预约数据
|
||||
@@ -137,7 +138,6 @@ async function fetchBookings(): Promise<void> {
|
||||
});
|
||||
bookingTable.value = table;
|
||||
}
|
||||
console.log(bookingTable.value,'bookingTable.value');
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取预约数据失败:', error);
|
||||
@@ -150,7 +150,8 @@ async function fetchBookings(): Promise<void> {
|
||||
function handleViewModeChange(e: RadioChangeEvent): void {
|
||||
viewMode.value = e.target.value;
|
||||
if (viewMode.value === 'date') {
|
||||
selectedDate.value = weekDates.value[0] ?? '';
|
||||
// 默认选中今天
|
||||
selectedDate.value = dayjs().format('YYYY-MM-DD');
|
||||
} else {
|
||||
selectedRoom.value = roomList.value[0]?.id ?? '';
|
||||
}
|
||||
|
1274
apps/web-antd/src/views/screen/digitalIntelligence/index.vue
Normal file
@@ -0,0 +1,890 @@
|
||||
<template>
|
||||
<div class="mian">
|
||||
<div class="title">
|
||||
<div class="left">
|
||||
<div class="left-first" id="time">--:--:--</div>
|
||||
<div class="left-second" id="date">----</div>
|
||||
</div>
|
||||
<div class="center">南川区综合服务中心数智管理平台能耗大屏</div>
|
||||
<div class="right">
|
||||
<div>{{ weekDay }}</div>
|
||||
<div>晴</div>
|
||||
<div>40℃</div>
|
||||
<div class="logout" @click="logout">
|
||||
<img src="../../../assets/return.png" style="width: 1.5rem; height: 1.5rem;">
|
||||
<div>
|
||||
退出
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用电量</span>
|
||||
<span class="header-value orange">180</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用电总量</span>
|
||||
<span class="header-value green">1.8</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用水总量</span>
|
||||
<span class="header-value blue">2600</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用水总量</span>
|
||||
<span class="header-value purple">30</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">设备总数</span>
|
||||
<span class="header-value green">500</span>
|
||||
<span class="header-unit">台</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contents">
|
||||
<div class="content">
|
||||
<div class="content-left">
|
||||
<div class="first">
|
||||
<div class="first-total">
|
||||
<div class="first-total-title">今日用电量</div>
|
||||
<div class="first-total-value">
|
||||
<div class="first-total-value-number">1</div>
|
||||
<div class="first-total-value-number">2</div>
|
||||
<div class="first-total-value-number">3</div>
|
||||
</div>
|
||||
<div class="first-total-unit">kwh</div>
|
||||
</div>
|
||||
<div ref="barChart" class="bar-chart" style="width:100%;height:80%;"></div>
|
||||
</div>
|
||||
<div class="second">
|
||||
<div ref="powerChart" class="power-chart"></div>
|
||||
</div>
|
||||
<div class="third">
|
||||
<div class="env-cards">
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">PM2.5(μg/m³)</div>
|
||||
<div class="env-value">5</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">PM10(μg/m³)</div>
|
||||
<div class="env-value">120</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">噪声(dB(A))</div>
|
||||
<div class="env-value">50</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">一氧化碳(ppm)</div>
|
||||
<div class="env-value">10</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="envChart" class="env-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center">
|
||||
<div class="content-center-first">
|
||||
<div class="first-item">
|
||||
<div class="item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center-second">
|
||||
<div class="second-item">
|
||||
<div class="second-item-text">645</div>
|
||||
<div class="second-item-text">729</div>
|
||||
<div class="second-item-text">648</div>
|
||||
<div class="second-item-text">786</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-right">
|
||||
<div class="first">
|
||||
<div class="first-total">
|
||||
<div class="first-total-title">今日用水量</div>
|
||||
<div class="first-total-value">
|
||||
<div class="first-total-value-number">1</div>
|
||||
<div class="first-total-value-number">2</div>
|
||||
<div class="first-total-value-number">3</div>
|
||||
</div>
|
||||
<div class="first-total-unit">吨</div>
|
||||
</div>
|
||||
<div ref="pie3dChart" class="pie3d-chart"></div>
|
||||
</div>
|
||||
<div class="second">
|
||||
<div ref="waterChart" class="water-chart"></div>
|
||||
</div>
|
||||
<div class="third">
|
||||
<div class="device-cards">
|
||||
<div class="device-card1">
|
||||
<div>
|
||||
<img src="../../../assets/energyConsumptionAnalysis/devices-number-icon.png" style="width: 1.75rem;height: 1.75rem;" alt="">
|
||||
</div>
|
||||
<div class="device-card-text-box">
|
||||
<div class="device-card-text">设备总数</div>
|
||||
<div class="device-card1-value">650</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-card2">
|
||||
<div>
|
||||
<img src="../../../assets/energyConsumptionAnalysis/devices-online-icon.png" style="width: 1.75rem;height: 1.75rem;" alt="">
|
||||
</div>
|
||||
<div>
|
||||
<div class="device-card-text">设备在线数</div>
|
||||
<div class="device-card2-value">632</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-card3">
|
||||
<div>
|
||||
<img src="../../../assets/energyConsumptionAnalysis/devices-offline-icon.png" style="width: 1.75rem;height: 1.75rem;" alt="">
|
||||
</div>
|
||||
<div>
|
||||
<div class="device-card-text">设备离线数</div>
|
||||
<div class="device-card3-value">18</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="deviceChart" class="device-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl'
|
||||
import { getThreeDBarOption } from '#/utils/threeDBarOption'
|
||||
import { renderPie3DChart } from '#/utils/pie3d'
|
||||
import { addChartToResizeManager, removeChartFromResizeManager } from '#/utils/echartsResize'
|
||||
|
||||
// 路由
|
||||
const router = useRouter()
|
||||
|
||||
// 图表实例
|
||||
const barChart = ref<HTMLElement>()
|
||||
const powerChart = ref<HTMLElement>()
|
||||
const envChart = ref<HTMLElement>()
|
||||
const waterChart = ref<HTMLElement>()
|
||||
const deviceChart = ref<HTMLElement>()
|
||||
const pie3dChart = ref<HTMLElement>()
|
||||
|
||||
// 定时器
|
||||
let timer: number | null = null
|
||||
|
||||
// 星期几
|
||||
const weekDay = ref('')
|
||||
|
||||
// 图表实例
|
||||
let barChartInstance: echarts.ECharts | null = null
|
||||
let powerChartInstance: echarts.ECharts | null = null
|
||||
let envChartInstance: echarts.ECharts | null = null
|
||||
let waterChartInstance: echarts.ECharts | null = null
|
||||
let deviceChartInstance: echarts.ECharts | null = null
|
||||
let pie3dChartInstance: any = null
|
||||
|
||||
// 退出方法
|
||||
const logout = () => {
|
||||
router.push('/navigation')
|
||||
}
|
||||
|
||||
// 更新时间
|
||||
const updateTime = () => {
|
||||
const now = new Date()
|
||||
const time = now.toLocaleTimeString('zh-CN', { hour12: false })
|
||||
const date = now.getFullYear() + '.' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '.' +
|
||||
String(now.getDate()).padStart(2, '0')
|
||||
|
||||
// 获取星期几
|
||||
const weekDays = ['日', '一', '二', '三', '四', '五', '六']
|
||||
weekDay.value = '星期' + weekDays[now.getDay()]
|
||||
|
||||
const timeElement = document.getElementById('time')
|
||||
const dateElement = document.getElementById('date')
|
||||
|
||||
if (timeElement) timeElement.innerText = time
|
||||
if (dateElement) dateElement.innerText = date
|
||||
}
|
||||
|
||||
// 初始化柱状图
|
||||
const initBarChart = () => {
|
||||
if (!barChart.value) return
|
||||
|
||||
const chart = echarts.init(barChart.value)
|
||||
const option = getThreeDBarOption({
|
||||
xData: ['A区', 'B区', 'C区', 'D区'],
|
||||
yData: [320, 452, 688, 400]
|
||||
})
|
||||
chart.setOption(option)
|
||||
barChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化电力图表
|
||||
const initPowerChart = () => {
|
||||
if (!powerChart.value) return
|
||||
|
||||
const chart = echarts.init(powerChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [{
|
||||
data: [80, 120, 100, 130, 150, 180, 200, 220, 250, 285, 230, 200, 180, 150, 120, 100, 80, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
}]
|
||||
}
|
||||
chart.setOption(option)
|
||||
powerChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化环境图表
|
||||
const initEnvChart = () => {
|
||||
if (!envChart.value) return
|
||||
|
||||
const chart = echarts.init(envChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 10, bottom: 20 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 8}, (_, i) => (i+1)*3),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今年',
|
||||
data: [100, 200, 150, 300, 250, 200, 100, 80],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#b388ff' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(179,136,255,0.2)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
},
|
||||
{
|
||||
name: '去年',
|
||||
data: [80, 120, 100, 180, 150, 120, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#ffb300' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(255,179,0,0.15)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
envChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化水表图表
|
||||
const initWaterChart = () => {
|
||||
if (!waterChart.value) return
|
||||
|
||||
const chart = echarts.init(waterChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
data: ['今日', '平均'],
|
||||
textStyle: { color: '#fff' },
|
||||
right: 20,
|
||||
top: 10,
|
||||
bottom: 10
|
||||
},
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今日',
|
||||
data: [1.2, 2.1, 1.8, 2.5, 3.0, 3.8, 4.2, 4.5, 4.8, 4.8, 4.0, 3.2, 2.5, 2.0, 1.5, 1.0, 0.8, 0.5, 0.2],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
},
|
||||
{
|
||||
name: '平均',
|
||||
data: Array(19).fill(2.5),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: { color: '#ff6b00' },
|
||||
lineStyle: { width: 1, type: 'solid', color: '#ff6b00' }
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
waterChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化设备图表
|
||||
const initDeviceChart = () => {
|
||||
if (!deviceChart.value) return
|
||||
|
||||
const chart = echarts.init(deviceChart.value)
|
||||
const option = {
|
||||
grid: { left: 60, right: 40, top: 10, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { show: false }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['门禁', '监控', '水表'],
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#A1B2C2' }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '设备数',
|
||||
type: 'bar',
|
||||
data: [650, 120, 40],
|
||||
barWidth: 12,
|
||||
itemStyle: {
|
||||
color: function(params: any) {
|
||||
const colors = [
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#2986B1' },
|
||||
{ offset: 1, color: '#6941FF' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#33FF99' },
|
||||
{ offset: 1, color: '#00908E' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#01B4FF' },
|
||||
{ offset: 1, color: '#0336FF' }
|
||||
]
|
||||
),
|
||||
];
|
||||
return colors[params.dataIndex]
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
deviceChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化3D饼图
|
||||
const initPie3DChart = () => {
|
||||
if (!pie3dChart.value) return
|
||||
|
||||
pie3dChartInstance = renderPie3DChart(pie3dChart.value, {
|
||||
data: [
|
||||
{ name: 'A区域', value: 20, itemStyle: { color: '#3ffbff' } },
|
||||
{ name: 'D区域', value: 20, itemStyle: { color: '#b388ff' } },
|
||||
{ name: 'C区域', value: 45, itemStyle: { color: '#ff9900' } },
|
||||
{ name: 'B区域', value: 15, itemStyle: { color: '#3f6bff' } },
|
||||
],
|
||||
hoverHeightScale: 2,
|
||||
selectOffset: 0.1,
|
||||
distance: 220,
|
||||
boxHeight: 5
|
||||
})
|
||||
if (pie3dChartInstance) {
|
||||
addChartToResizeManager(pie3dChartInstance)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
updateTime()
|
||||
timer = setInterval(updateTime, 1000)
|
||||
initBarChart()
|
||||
initPowerChart()
|
||||
initEnvChart()
|
||||
initWaterChart()
|
||||
initDeviceChart()
|
||||
initPie3DChart()
|
||||
})
|
||||
|
||||
// 组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
|
||||
// 从管理器中移除图表
|
||||
if (barChartInstance) {
|
||||
removeChartFromResizeManager(barChartInstance)
|
||||
barChartInstance.dispose()
|
||||
}
|
||||
if (powerChartInstance) {
|
||||
removeChartFromResizeManager(powerChartInstance)
|
||||
powerChartInstance.dispose()
|
||||
}
|
||||
if (envChartInstance) {
|
||||
removeChartFromResizeManager(envChartInstance)
|
||||
envChartInstance.dispose()
|
||||
}
|
||||
if (waterChartInstance) {
|
||||
removeChartFromResizeManager(waterChartInstance)
|
||||
waterChartInstance.dispose()
|
||||
}
|
||||
if (deviceChartInstance) {
|
||||
removeChartFromResizeManager(deviceChartInstance)
|
||||
deviceChartInstance.dispose()
|
||||
}
|
||||
if (pie3dChartInstance) {
|
||||
removeChartFromResizeManager(pie3dChartInstance)
|
||||
pie3dChartInstance.dispose()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mian{
|
||||
height: 100vh;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/bg.png");
|
||||
background-size: 100% 100%;
|
||||
background-color: #081b3a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.title {
|
||||
height: 5.375rem;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.left{
|
||||
display: flex;
|
||||
width: 14.3125rem;
|
||||
.left-first{
|
||||
padding-left: 2.3125rem;
|
||||
padding-right: 3.5rem;
|
||||
font-size: 1.875rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.left-second{
|
||||
width: 6.5rem;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
.center{
|
||||
font-size: 1.9rem;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.1em;
|
||||
text-shadow: 0 0 10px #1e90ff, 0 0 20px #1e90ff;
|
||||
}
|
||||
.right{
|
||||
width: 17.3125rem;
|
||||
display: flex;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
gap: .75rem;
|
||||
.logout{
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
gap: .2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.header{
|
||||
/* margin-top: 1.125rem; */
|
||||
margin-left: 4.625rem;
|
||||
margin-right: 4.25rem;
|
||||
height: 4.4rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.header-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 0 10px #0ff2, 0 0 20px #0ff2 inset;
|
||||
}
|
||||
.header-label {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-right: 0.5rem;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.header-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
|
||||
.header-value.orange { color: #ffb300; }
|
||||
.header-value.green { color: #00ffb0; }
|
||||
.header-value.blue { color: #3ec6ff; }
|
||||
.header-value.purple { color: #b388ff; }
|
||||
|
||||
.header-unit {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
}
|
||||
.header div{
|
||||
width: 20.125rem;
|
||||
}
|
||||
.content{
|
||||
flex:1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0 2.25rem 2.25rem 2.25rem;
|
||||
.content-left{
|
||||
width: 32.6875rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 13.9rem;
|
||||
.first-total{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 1rem 1rem 0 0;
|
||||
font-size: 1.12rem;
|
||||
color: #89EAFF;
|
||||
.first-total-title{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: 1.12rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.first-total-value{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.first-total-value-number{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: bold;
|
||||
font-size: 1.3rem;
|
||||
color: #89EAFF;
|
||||
line-height: 1rem;
|
||||
padding: .3rem;
|
||||
margin: 0 .1rem;
|
||||
border: .02rem solid rgba(99,145,180,0.59);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 12.5rem;
|
||||
/* margin-top:2.25rem;
|
||||
margin-bottom: 2rem; */
|
||||
.power-chart {
|
||||
height: 100%;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
}
|
||||
.third{
|
||||
height: 16rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* justify-content: space-around; */
|
||||
.env-cards {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 5rem;
|
||||
margin-left: 1rem;
|
||||
margin-right: 1.1rem;
|
||||
.env-card {
|
||||
width: 6.9rem;
|
||||
height: 3.2rem;
|
||||
border-radius: 0.5rem;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
.env-card-content{
|
||||
.env-title {
|
||||
font-size: .9rem;
|
||||
font-weight: 400;
|
||||
color: #4CE2D1;
|
||||
}
|
||||
.env-value {
|
||||
font-size: .8rem;
|
||||
font-weight: bold;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.env-chart {
|
||||
height: 8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-center{
|
||||
width: 47.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.content-center-first{
|
||||
height: 31.9rem;
|
||||
.first-item{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/center-bg.png");
|
||||
background-size: 95% 95%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
.content-center-second{
|
||||
height: 12.5rem;
|
||||
.second-item{
|
||||
margin-top: 6rem;
|
||||
margin-left: 3.68rem;
|
||||
margin-bottom: 6.5rem;
|
||||
margin-right: 4.9rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.second-item-text{
|
||||
width: 5.37rem;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-right{
|
||||
width: 32.68rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 14.9rem;
|
||||
.first-total{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 1rem 1rem 0 0;
|
||||
font-size: 1.12rem;
|
||||
color: #89EAFF;
|
||||
.first-total-title{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: 1.12rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.first-total-value{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.first-total-value-number{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: bold;
|
||||
font-size: 1.3rem;
|
||||
color: #89EAFF;
|
||||
line-height: 1rem;
|
||||
padding: .3rem;
|
||||
margin: 0 .1rem;
|
||||
border: .02rem solid rgba(99,145,180,0.59);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 12.5rem;
|
||||
/* margin-top:2.25rem;
|
||||
margin-bottom: 2rem; */
|
||||
.water-chart {
|
||||
height: 100%;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
}
|
||||
.third{
|
||||
height: 14rem;
|
||||
.device-cards {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 2rem;
|
||||
margin-right: 1.06rem;
|
||||
/* margin-bottom: 0.5rem; */
|
||||
margin-left: 1.31rem;
|
||||
.device-card1{
|
||||
height: 3.2rem;
|
||||
width: 7.21rem;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/devices-number.png");
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.device-card1-value{
|
||||
font-family: Arial;
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
color: #02B3F4;
|
||||
}
|
||||
}
|
||||
.device-card2{
|
||||
height: 3.2rem;
|
||||
width: 7.21rem;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/devices-online.png");
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.device-card2-value{
|
||||
font-family: Arial;
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
color: #1DE39D;
|
||||
}
|
||||
}
|
||||
.device-card3{
|
||||
height: 3.2rem;
|
||||
width: 7.21rem;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/devices-offline.png");
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.device-card3-value{
|
||||
font-family: Arial;
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
color: #F19315;
|
||||
}
|
||||
}
|
||||
.device-card-text{
|
||||
font-family: Microsoft YaHei UI;
|
||||
font-weight: 400;
|
||||
font-size: 0.75rem;
|
||||
color: #A1B2C2;
|
||||
line-height: 0.75rem;
|
||||
}
|
||||
}
|
||||
.device-chart {
|
||||
height: 8rem;
|
||||
/* margin-top: 0.2rem; */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pie3d-title {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.pie3d-water {
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.pie3d-water-num {
|
||||
font-size: 1.3rem;
|
||||
color: #3ec6ff;
|
||||
background: #0a1e3a;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0 0.2rem;
|
||||
margin: 0 0.2rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.pie3d-chart {
|
||||
width: 100%;
|
||||
height: 176px;
|
||||
min-height: unset;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
1283
apps/web-antd/src/views/screen/monitor/index.vue
Normal file
307
apps/web-antd/src/views/screen/navigation/Navigation.vue
Normal file
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div class="navigation-bg">
|
||||
<div class="navigation-head">
|
||||
<div class="navigation-head-left">超级管理员 欢迎你</div>
|
||||
<div class="navigation-head-center">南川区综合服务中心数智平台</div>
|
||||
<div class="navigation-head-right" @click="logout">
|
||||
<span>
|
||||
<!-- <img src="../../../assets/return.png" alt="退出"> -->
|
||||
退出
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navigation-body">
|
||||
<div class="navigation-body-left">
|
||||
<div @click="goToProperty()">基础物业</div>
|
||||
<div @click="goToEnergyAnalysis()">能耗分析</div>
|
||||
<div @click="goToSecurity()">安防大屏</div>
|
||||
<div @click="goToMonitor()">监控大屏</div>
|
||||
</div>
|
||||
<div class="navigation-body-center">
|
||||
<div class="top-content">
|
||||
<div class="top-content-text1"><div class="text1">数据管理</div></div>
|
||||
<div class="top-content-text2"><div class="text1">运行监控</div></div>
|
||||
<div class="top-content-text3"><div class="text1">信息管理</div></div>
|
||||
<div class="top-content-text4"><div class="text1">配置管理</div></div>
|
||||
<div class="top-content-text5"><div class="text1">登录管理</div></div>
|
||||
<div class="top-content-text6"><div class="text1">驾驶舱</div></div>
|
||||
</div>
|
||||
<div class="bottom-content">
|
||||
<div class="bottom-content-text1"><div class="text">数字建模</div></div>
|
||||
<div class="bottom-content-text2"><div class="text">指挥中心</div></div>
|
||||
<div class="bottom-content-text3"><div class="text">软件支撑</div></div>
|
||||
<div class="bottom-content-text4"><div class="text">应用管理</div></div>
|
||||
<div class="bottom-content-text5"><div class="text">数据管理</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navigation-body-right">
|
||||
<div @click="goToDigitalIntelligence()">商务中心</div>
|
||||
<div @click="goToHome()">后台管理</div>
|
||||
<div>能源管理</div>
|
||||
<div>能耗分析</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 退出方法
|
||||
const logout = () => {
|
||||
// 这里可以添加清除 token、重定向登录页等逻辑
|
||||
console.log('用户退出')
|
||||
router.push('/login') // 假设登录页面路径为 /login
|
||||
}
|
||||
//物业
|
||||
const goToProperty = () => {
|
||||
router.push('/property')
|
||||
}
|
||||
//能耗分析
|
||||
const goToEnergyAnalysis = () => {
|
||||
router.push('/energyAnalysis')
|
||||
}
|
||||
// 监控大屏
|
||||
const goToSecurity = () => {
|
||||
router.push('/security')
|
||||
}
|
||||
// 监控大屏
|
||||
const goToMonitor = () => {
|
||||
router.push('/monitor')
|
||||
}
|
||||
// 商务中心数智管理平台
|
||||
const goToDigitalIntelligence = () => {
|
||||
router.push('/digitalIntelligence')
|
||||
}
|
||||
//
|
||||
const goToHome = () => {
|
||||
router.push(preferences.app.defaultHomePath);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.navigation-bg {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
background: url("../../../assets/navigation.png");
|
||||
background-size: 100% 100%;
|
||||
color: #fff;
|
||||
text-shadow: 0 0.125rem 0.5rem #000;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navigation-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: 3.5rem;
|
||||
}
|
||||
|
||||
.navigation-head-center{
|
||||
font-size: 1.9rem;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.navigation-head-left,
|
||||
.navigation-head-right {
|
||||
flex: 1 1 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
padding-left: 1.3rem;
|
||||
padding-right: 1.3rem;
|
||||
}
|
||||
|
||||
@media (min-width: 48em) {
|
||||
.navigation-head-left {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
font-size: 0.875rem;
|
||||
padding-left: 1.3rem;
|
||||
}
|
||||
.navigation-head-center {
|
||||
flex: 3;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.navigation-head-right {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
font-size: 0.875rem;
|
||||
padding-right: 1.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.navigation-body-left {
|
||||
width: 12%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
padding: 2.25rem 1.25rem;
|
||||
height: 35rem;
|
||||
}
|
||||
|
||||
.navigation-body-left div {
|
||||
background: url("../../../assets/shine.png") no-repeat center;
|
||||
background-size: contain;
|
||||
font-size: 1rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
min-width: 7.5rem;
|
||||
max-width: 100%;
|
||||
transition: transform 0.2s ease;
|
||||
cursor: pointer;
|
||||
color: #77DAFF;
|
||||
}
|
||||
.navigation-body-left div:hover {
|
||||
color: #ffffff;
|
||||
background-image: url("../../../assets/shine-lines.png");
|
||||
}
|
||||
|
||||
.navigation-body-center{
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.top-content{
|
||||
height: 70%;
|
||||
padding-top: 26rem;
|
||||
padding-left: 19.5rem;
|
||||
padding-right: 20rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.top-content-text1{
|
||||
font-size: 1.0625rem;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 0.5rem;
|
||||
color: #86B6E6;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text2{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 1.25rem;
|
||||
color: #86ECDE;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text3{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 2.5rem;
|
||||
color: #FECFB4;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text4{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 2.625rem;
|
||||
color: #CDADF4;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text5{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 1.5625rem;
|
||||
color: #86ECDE;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text6{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 1.25rem;
|
||||
color: #86B6E6;
|
||||
cursor: pointer;
|
||||
}
|
||||
.text1{
|
||||
padding: 0.3125rem 1.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-content{
|
||||
height: 50%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 12.5rem;
|
||||
padding-left: 23.625rem;
|
||||
padding-right: 24.25rem;
|
||||
font-size: 1rem;
|
||||
color: #00EEFD;
|
||||
.bottom-content-text1{
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text2{
|
||||
padding-top: 0.75rem;
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text3{
|
||||
padding-top: 1.125rem;
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text4{
|
||||
padding-top: 0.8125rem;
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text5{
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.text{
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.navigation-body-right{
|
||||
width: 12%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
padding: 2.25rem 1.25rem;
|
||||
height: 35rem;
|
||||
}
|
||||
.navigation-body-right div {
|
||||
background: url("../../../assets/shine.png") no-repeat center;
|
||||
background-size: contain;
|
||||
font-size: 1rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
min-width: 7.5rem;
|
||||
max-width: 100%;
|
||||
transition: transform 0.2s ease;
|
||||
cursor: pointer;
|
||||
color: #77DAFF;
|
||||
}
|
||||
.navigation-body-right div:hover {
|
||||
background-image: url("../../../assets/shine-lines.png");
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
804
apps/web-antd/src/views/screen/property/index.vue
Normal file
@@ -0,0 +1,804 @@
|
||||
<template>
|
||||
<div class="mian">
|
||||
<div class="title">
|
||||
<div class="left">
|
||||
<div class="left-first" id="time">--:--:--</div>
|
||||
<div class="left-second" id="date">----</div>
|
||||
</div>
|
||||
<div class="center">南川区综合服务中心数智管理平台物业大屏</div>
|
||||
<div class="right">
|
||||
<div>{{ weekDay }}</div>
|
||||
<div>晴</div>
|
||||
<div>40℃</div>
|
||||
<div class="logout" @click="logout">
|
||||
<img src="../../../assets/return.png" style="width: 1.5rem; height: 1.5rem;">
|
||||
<div>
|
||||
退出
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用电量</span>
|
||||
<span class="header-value orange">180</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用电总量</span>
|
||||
<span class="header-value green">1.8</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用水总量</span>
|
||||
<span class="header-value blue">2600</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用水总量</span>
|
||||
<span class="header-value purple">30</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">设备总数</span>
|
||||
<span class="header-value green">500</span>
|
||||
<span class="header-unit">台</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contents">
|
||||
<div class="content">
|
||||
<div class="content-left">
|
||||
<div class="first">
|
||||
<div ref="barChart" class="bar-chart" style="width:100%;height:100%;"></div>
|
||||
</div>
|
||||
<div class="second">
|
||||
<div ref="powerChart" class="power-chart"></div>
|
||||
</div>
|
||||
<div class="third">
|
||||
<div ref="envChart" class="env-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center">
|
||||
<div class="content-center-first">
|
||||
<div class="first-item">
|
||||
<div class="item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center-second">
|
||||
<div class="second-item">
|
||||
<div class="second-item-box1">645</div>
|
||||
<div class="second-item-box2">729</div>
|
||||
<div class="second-item-box3">648</div>
|
||||
<div class="second-item-box4">786</div>
|
||||
<div class="second-item-box5">645</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-right">
|
||||
<div class="first">
|
||||
<div ref="waterChart" class="water-chart"></div>
|
||||
<!-- <div ref="pie3dChart" class="pie3d-chart"></div> -->
|
||||
</div>
|
||||
<div class="second">
|
||||
<div class="second-box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-label">服务数量</div>
|
||||
<div class="box-content-num">132</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-label">服务数量</div>
|
||||
<div class="box-content-num">862</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-label">服务数量</div>
|
||||
<div class="box-content-num">272</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div ref="waterChart" class="water-chart"></div> -->
|
||||
</div>
|
||||
<div class="third">
|
||||
<div ref="deviceChart" class="device-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl'
|
||||
import { getThreeDBarOption } from '#/utils/threeDBarOption'
|
||||
import { renderPie3DChart } from '#/utils/pie3d'
|
||||
import { addChartToResizeManager, removeChartFromResizeManager } from '#/utils/echartsResize'
|
||||
|
||||
// 路由
|
||||
const router = useRouter()
|
||||
|
||||
// 图表实例
|
||||
const barChart = ref<HTMLElement>()
|
||||
const powerChart = ref<HTMLElement>()
|
||||
const envChart = ref<HTMLElement>()
|
||||
const waterChart = ref<HTMLElement>()
|
||||
const deviceChart = ref<HTMLElement>()
|
||||
const pie3dChart = ref<HTMLElement>()
|
||||
|
||||
// 定时器
|
||||
let timer: number | null = null
|
||||
|
||||
// 星期几
|
||||
const weekDay = ref('')
|
||||
|
||||
// 图表实例
|
||||
let barChartInstance: echarts.ECharts | null = null
|
||||
let powerChartInstance: echarts.ECharts | null = null
|
||||
let envChartInstance: echarts.ECharts | null = null
|
||||
let waterChartInstance: echarts.ECharts | null = null
|
||||
let deviceChartInstance: echarts.ECharts | null = null
|
||||
let pie3dChartInstance: any = null
|
||||
|
||||
// 退出方法
|
||||
const logout = () => {
|
||||
router.push('/navigation')
|
||||
}
|
||||
|
||||
// 更新时间
|
||||
const updateTime = () => {
|
||||
const now = new Date()
|
||||
const time = now.toLocaleTimeString('zh-CN', { hour12: false })
|
||||
const date = now.getFullYear() + '.' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '.' +
|
||||
String(now.getDate()).padStart(2, '0')
|
||||
|
||||
// 获取星期几
|
||||
const weekDays = ['日', '一', '二', '三', '四', '五', '六']
|
||||
weekDay.value = '星期' + weekDays[now.getDay()]
|
||||
|
||||
const timeElement = document.getElementById('time')
|
||||
const dateElement = document.getElementById('date')
|
||||
|
||||
if (timeElement) timeElement.innerText = time
|
||||
if (dateElement) dateElement.innerText = date
|
||||
}
|
||||
|
||||
// 初始化柱状图
|
||||
const initBarChart = () => {
|
||||
if (!barChart.value) return
|
||||
|
||||
const chart = echarts.init(barChart.value)
|
||||
const option = getThreeDBarOption({
|
||||
xData: ['A区', 'B区', 'C区', 'D区'],
|
||||
yData: [320, 452, 688, 400]
|
||||
})
|
||||
chart.setOption(option)
|
||||
barChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化电力图表
|
||||
const initPowerChart = () => {
|
||||
if (!powerChart.value) return
|
||||
|
||||
const chart = echarts.init(powerChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 32, bottom: 20 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [{
|
||||
data: [80, 120, 100, 130, 150, 180, 200, 220, 250, 285, 230, 200, 180, 150, 120, 100, 80, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
}]
|
||||
}
|
||||
chart.setOption(option)
|
||||
powerChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化环境图表
|
||||
const initEnvChart = () => {
|
||||
if (!envChart.value) return
|
||||
|
||||
const chart = echarts.init(envChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 8}, (_, i) => (i+1)*3),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今年',
|
||||
data: [100, 200, 150, 300, 250, 200, 100, 80],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#b388ff' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(179,136,255,0.2)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
},
|
||||
{
|
||||
name: '去年',
|
||||
data: [80, 120, 100, 180, 150, 120, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#ffb300' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(255,179,0,0.15)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
envChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化水表图表
|
||||
const initWaterChart = () => {
|
||||
if (!waterChart.value) return
|
||||
|
||||
const chart = echarts.init(waterChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
data: ['今日', '平均'],
|
||||
textStyle: { color: '#fff' },
|
||||
right: 20,
|
||||
top: 10
|
||||
},
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今日',
|
||||
data: [1.2, 2.1, 1.8, 2.5, 3.0, 3.8, 4.2, 4.5, 4.8, 4.8, 4.0, 3.2, 2.5, 2.0, 1.5, 1.0, 0.8, 0.5, 0.2],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
},
|
||||
{
|
||||
name: '平均',
|
||||
data: Array(19).fill(2.5),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: { color: '#ff6b00' },
|
||||
lineStyle: { width: 1, type: 'solid', color: '#ff6b00' }
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
waterChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化设备图表
|
||||
const initDeviceChart = () => {
|
||||
if (!deviceChart.value) return
|
||||
|
||||
const chart = echarts.init(deviceChart.value)
|
||||
const option = {
|
||||
grid: { left: 60, right: 40, top: 10, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { show: false }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['门禁', '监控', '水表'],
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#A1B2C2' }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '设备数',
|
||||
type: 'bar',
|
||||
data: [650, 120, 40],
|
||||
barWidth: 12,
|
||||
itemStyle: {
|
||||
color: function(params: any) {
|
||||
const colors = [
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#2986B1' },
|
||||
{ offset: 1, color: '#6941FF' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#33FF99' },
|
||||
{ offset: 1, color: '#00908E' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#01B4FF' },
|
||||
{ offset: 1, color: '#0336FF' }
|
||||
]
|
||||
),
|
||||
];
|
||||
return colors[params.dataIndex]
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
deviceChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化3D饼图
|
||||
const initPie3DChart = () => {
|
||||
if (!pie3dChart.value) return
|
||||
|
||||
pie3dChartInstance = renderPie3DChart(pie3dChart.value, {
|
||||
data: [
|
||||
{ name: 'A区域', value: 20, itemStyle: { color: '#3ffbff' } },
|
||||
{ name: 'D区域', value: 20, itemStyle: { color: '#b388ff' } },
|
||||
{ name: 'C区域', value: 45, itemStyle: { color: '#ff9900' } },
|
||||
{ name: 'B区域', value: 15, itemStyle: { color: '#3f6bff' } },
|
||||
],
|
||||
hoverHeightScale: 2,
|
||||
selectOffset: 0.1,
|
||||
distance: 220,
|
||||
boxHeight: 5
|
||||
})
|
||||
if (pie3dChartInstance) {
|
||||
addChartToResizeManager(pie3dChartInstance)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
updateTime()
|
||||
timer = setInterval(updateTime, 1000)
|
||||
initBarChart()
|
||||
initPowerChart()
|
||||
initEnvChart()
|
||||
initWaterChart()
|
||||
initDeviceChart()
|
||||
initPie3DChart()
|
||||
})
|
||||
|
||||
// 组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
|
||||
// 从管理器中移除图表
|
||||
if (barChartInstance) {
|
||||
removeChartFromResizeManager(barChartInstance)
|
||||
barChartInstance.dispose()
|
||||
}
|
||||
if (powerChartInstance) {
|
||||
removeChartFromResizeManager(powerChartInstance)
|
||||
powerChartInstance.dispose()
|
||||
}
|
||||
if (envChartInstance) {
|
||||
removeChartFromResizeManager(envChartInstance)
|
||||
envChartInstance.dispose()
|
||||
}
|
||||
if (waterChartInstance) {
|
||||
removeChartFromResizeManager(waterChartInstance)
|
||||
waterChartInstance.dispose()
|
||||
}
|
||||
if (deviceChartInstance) {
|
||||
removeChartFromResizeManager(deviceChartInstance)
|
||||
deviceChartInstance.dispose()
|
||||
}
|
||||
if (pie3dChartInstance) {
|
||||
removeChartFromResizeManager(pie3dChartInstance)
|
||||
pie3dChartInstance.dispose()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mian{
|
||||
height: 100vh;
|
||||
background: url("../../../assets/property/bg.png");
|
||||
background-size: 100% 100%;
|
||||
background-color: #081b3a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.title {
|
||||
height: 5.375rem;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.left{
|
||||
display: flex;
|
||||
width: 14.3125rem;
|
||||
.left-first{
|
||||
padding-left: 2.3125rem;
|
||||
padding-right: 3.5rem;
|
||||
font-size: 1.875rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.left-second{
|
||||
width: 6.5rem;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
.center{
|
||||
font-size: 1.9rem;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.1em;
|
||||
text-shadow: 0 0 10px #1e90ff, 0 0 20px #1e90ff;
|
||||
}
|
||||
.right{
|
||||
width: 17.3125rem;
|
||||
display: flex;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
gap: .75rem;
|
||||
.logout{
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
gap: .2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.header{
|
||||
margin-top: 1.125rem;
|
||||
margin-left: 4.625rem;
|
||||
margin-right: 4.25rem;
|
||||
height: 6rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.header-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 0 10px #0ff2, 0 0 20px #0ff2 inset;
|
||||
}
|
||||
.header-label {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-right: 0.5rem;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.header-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
|
||||
.header-value.orange { color: #ffb300; }
|
||||
.header-value.green { color: #00ffb0; }
|
||||
.header-value.blue { color: #3ec6ff; }
|
||||
.header-value.purple { color: #b388ff; }
|
||||
|
||||
.header-unit {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
}
|
||||
.header div{
|
||||
width: 20.125rem;
|
||||
}
|
||||
.contents{
|
||||
flex: 1;
|
||||
.content{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0 2.25rem 2.25rem 2.25rem;
|
||||
.content-left{
|
||||
width: 32.6875rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 16.81rem;
|
||||
.pie3d-title {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.pie3d-water {
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.pie3d-water-num {
|
||||
font-size: 1.3rem;
|
||||
color: #3ec6ff;
|
||||
background: #0a1e3a;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0 0.2rem;
|
||||
margin: 0 0.2rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.pie3d-chart {
|
||||
width: 100%;
|
||||
height: 176px;
|
||||
min-height: unset;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 12rem;
|
||||
/* margin-top:2.25rem; */
|
||||
/* margin-bottom: 2rem; */
|
||||
.power-chart {
|
||||
height: 100%;
|
||||
/* margin-top: 0.2rem; */
|
||||
}
|
||||
}
|
||||
.third{
|
||||
height: 12.12rem;
|
||||
.env-chart {
|
||||
height: 12rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-center{
|
||||
width: 47.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.content-center-first{
|
||||
height: 30.9rem;
|
||||
.first-item{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: url("../../../assets/property/center-bg.png");
|
||||
background-size: 95% 95%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
.content-center-second{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 12.5rem;
|
||||
.second-item{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
.second-item-box1{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle1.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box2{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle2.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box3{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle3.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box4{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle1.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box5{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle4.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-right{
|
||||
width: 32.68rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 16.81rem;
|
||||
.water-chart {
|
||||
height: 100%;
|
||||
/* margin-top: 0.2rem; */
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 11rem;
|
||||
margin-top:2rem;
|
||||
margin-bottom: 2rem;
|
||||
.second-box{
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
.box-content{
|
||||
height: 8rem;
|
||||
width: 8rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.box-content-label{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: .8rem;
|
||||
color: #C4D6FF;
|
||||
line-height: 2rem;
|
||||
}
|
||||
.box-content-num{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
background: url("../../../assets/property/customer-circle.png");
|
||||
background-size: 100% 100%;
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: 1.2rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.third{
|
||||
height: 13.12rem;
|
||||
.device-chart {
|
||||
height: 10rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pie3d-title {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.pie3d-water {
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.pie3d-water-num {
|
||||
font-size: 1.3rem;
|
||||
color: #3ec6ff;
|
||||
background: #0a1e3a;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0 0.2rem;
|
||||
margin: 0 0.2rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.pie3d-chart {
|
||||
width: 100%;
|
||||
height: 176px;
|
||||
min-height: unset;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
1182
apps/web-antd/src/views/screen/security/index.vue
Normal file
@@ -27,7 +27,7 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
// 默认占满两列
|
||||
formItemClass: 'col-span-1',
|
||||
// 默认label宽度 px
|
||||
labelWidth: 80,
|
||||
labelWidth: 120,
|
||||
// 通用配置项 会影响到所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
|
@@ -84,10 +84,6 @@ export const columns: VxeGridProps['columns'] = [
|
||||
title: '外部编码',
|
||||
field: 'outCode',
|
||||
},
|
||||
{
|
||||
title: '绑定设备',
|
||||
field: 'bindDeviceIp',
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
@@ -147,9 +143,10 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
},
|
||||
{
|
||||
label: '绑定设备',
|
||||
fieldName: 'bindDeviceIp',
|
||||
fieldName: 'bindDeviceId',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
resultField: 'list', // 根据API返回结构调整
|
||||
labelField: 'deviceName',
|
||||
valueField: 'id',
|
||||
|
@@ -1,117 +0,0 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { DictEnum } from '@vben/constants';
|
||||
import { getPopupContainer } from '@vben/utils';
|
||||
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'eqpName',
|
||||
label: '设备名称',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
getPopupContainer,
|
||||
options: getDictOptions(DictEnum.SIS_ACCESS_CONTROL_DEVICE_TYPE),
|
||||
},
|
||||
fieldName: 'factoryNo',
|
||||
label: '设备厂商',
|
||||
},
|
||||
];
|
||||
|
||||
// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新
|
||||
// export const columns: () => VxeGridProps['columns'] = () => [
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{
|
||||
title: '设备编码',
|
||||
field: 'eqpNo',
|
||||
},
|
||||
{
|
||||
title: '设备名称',
|
||||
field: 'eqpName',
|
||||
},
|
||||
{
|
||||
title: '设备厂商',
|
||||
field: 'factoryName',
|
||||
},
|
||||
|
||||
{
|
||||
title: '设备ip',
|
||||
field: 'eqpIp',
|
||||
},
|
||||
{
|
||||
title: '设备端口',
|
||||
field: 'eqpPort',
|
||||
},
|
||||
{
|
||||
title: '设备账号',
|
||||
field: 'eqpAccount',
|
||||
},
|
||||
{
|
||||
title: '设备密码',
|
||||
field: 'eqpPwd',
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
slots: { default: 'action' },
|
||||
title: '操作',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
export const modalSchema: FormSchemaGetter = () => [
|
||||
{
|
||||
label: '主键',
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
show: () => false,
|
||||
triggerFields: [''],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '设备名称',
|
||||
fieldName: 'eqpName',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '设备厂商',
|
||||
fieldName: 'factoryNo',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
getPopupContainer,
|
||||
options: getDictOptions(DictEnum.SIS_ACCESS_CONTROL_DEVICE_TYPE),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '设备ip',
|
||||
fieldName: 'eqpIp',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '设备端口',
|
||||
fieldName: 'eqpPort',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '设备账号',
|
||||
fieldName: 'eqpAccount',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '设备密码',
|
||||
fieldName: 'eqpPwd',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
];
|
@@ -23,7 +23,7 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
// 默认占满两列
|
||||
formItemClass: 'col-span-1',
|
||||
// 默认label宽度 px
|
||||
labelWidth: 80,
|
||||
labelWidth: 120,
|
||||
// 通用配置项 会影响到所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
|
@@ -1,236 +0,0 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'eqpCode',
|
||||
label: '设备编码',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'video',
|
||||
label: '视频协议',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {},
|
||||
fieldName: 'transportType',
|
||||
label: '传输协议',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'channelName',
|
||||
label: '点位名称',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'rtspAddr',
|
||||
label: 'rtsp 地址',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'ip',
|
||||
label: '点位名称',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'port',
|
||||
label: '端口',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'username',
|
||||
label: '通道相机账号',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'pwd',
|
||||
label: '相机密码',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {},
|
||||
fieldName: 'videoType',
|
||||
label: '码流',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'videoId',
|
||||
label: '码流id',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'createById',
|
||||
label: '创建人id',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'updateById',
|
||||
label: '更新人id',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'searchValue',
|
||||
label: '搜索值',
|
||||
},
|
||||
];
|
||||
|
||||
// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新
|
||||
// export const columns: () => VxeGridProps['columns'] = () => [
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{
|
||||
title: '主键id',
|
||||
field: 'id',
|
||||
},
|
||||
{
|
||||
title: '设备编码',
|
||||
field: 'eqpCode',
|
||||
},
|
||||
{
|
||||
title: '视频协议',
|
||||
},
|
||||
{
|
||||
title: '传输协议',
|
||||
field: 'transportType',
|
||||
},
|
||||
{
|
||||
title: '点位名称',
|
||||
field: 'channelName',
|
||||
},
|
||||
{
|
||||
title: 'rtsp 地址',
|
||||
field: 'rtspAddr',
|
||||
},
|
||||
{
|
||||
title: '点位名称',
|
||||
field: 'ip',
|
||||
},
|
||||
{
|
||||
title: '端口',
|
||||
field: 'port',
|
||||
},
|
||||
{
|
||||
title: '通道相机账号',
|
||||
field: 'username',
|
||||
},
|
||||
{
|
||||
title: '相机密码',
|
||||
field: 'pwd',
|
||||
},
|
||||
{
|
||||
title: '设备码流',
|
||||
field: 'videoType',
|
||||
},
|
||||
{
|
||||
title: '码流id',
|
||||
field: 'videoId',
|
||||
},
|
||||
{
|
||||
title: '创建人id',
|
||||
field: 'createById',
|
||||
},
|
||||
{
|
||||
title: '更新人id',
|
||||
field: 'updateById',
|
||||
},
|
||||
{
|
||||
title: '搜索值',
|
||||
field: 'searchValue',
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'action',
|
||||
},
|
||||
title: '操作',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
export const modalSchema: FormSchemaGetter = () => [
|
||||
{
|
||||
label: '主键id',
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
show: () => false,
|
||||
triggerFields: [''],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '设备编码',
|
||||
fieldName: 'eqpCode',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '视频协议',
|
||||
fieldName: 'video',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '传输协议',
|
||||
fieldName: 'transportType',
|
||||
component: 'Select',
|
||||
componentProps: {},
|
||||
},
|
||||
{
|
||||
label: '点位名称',
|
||||
fieldName: 'channelName',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: 'rtsp 地址',
|
||||
fieldName: 'rtspAddr',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '点位名称',
|
||||
fieldName: 'ip',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '端口',
|
||||
fieldName: 'port',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '通道相机账号',
|
||||
fieldName: 'username',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '相机密码',
|
||||
fieldName: 'pwd',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '设备码流',
|
||||
fieldName: 'videoType',
|
||||
component: 'Select',
|
||||
componentProps: {},
|
||||
},
|
||||
{
|
||||
label: '码流id',
|
||||
fieldName: 'videoId',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '创建人id',
|
||||
fieldName: 'createById',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '更新人id',
|
||||
fieldName: 'updateById',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '搜索值',
|
||||
fieldName: 'searchValue',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
@@ -1,5 +1,7 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { DeviceManageQuery } from '#/api/sis/deviceManage/model';
|
||||
import { deviceManageList } from '#/api/sis/deviceManage';
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
@@ -58,18 +60,6 @@ export const columns: VxeGridProps['columns'] = [
|
||||
title: '维保公司',
|
||||
field: 'maintenanceCompany',
|
||||
},
|
||||
/*{
|
||||
title: '维保电话',
|
||||
field: 'maintenancePhone',
|
||||
},
|
||||
{
|
||||
title: '上次年检日期',
|
||||
field: 'lastInspectionDate',
|
||||
},
|
||||
{
|
||||
title: '下次年检日期',
|
||||
field: 'nextInspectionDate',
|
||||
},*/
|
||||
{
|
||||
title: '梯控厂商',
|
||||
field: 'controlFactory',
|
||||
@@ -173,31 +163,6 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
fieldName: 'maintenanceCompany',
|
||||
component: 'Input',
|
||||
},
|
||||
/* {
|
||||
label: '维保电话',
|
||||
fieldName: 'maintenancePhone',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '上次年检日期',
|
||||
fieldName: 'lastInspectionDate',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '下次年检日期',
|
||||
fieldName: 'nextInspectionDate',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},*/
|
||||
{
|
||||
label: '梯控厂商',
|
||||
fieldName: 'controlFactory',
|
||||
@@ -228,4 +193,18 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'remoteCallElevatorDeviceId',
|
||||
defaultValue: undefined,
|
||||
label: '呼梯摄像头',
|
||||
},
|
||||
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'elevatorControlDeviceId',
|
||||
defaultValue: undefined,
|
||||
label: '梯控摄像头',
|
||||
|
||||
},
|
||||
];
|
||||
|
@@ -6,11 +6,17 @@ import { $t } from '@vben/locales';
|
||||
import { cloneDeep, getPopupContainer, handleNode } from '@vben/utils';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { elevatorInfoAdd, elevatorInfoInfo, elevatorInfoUpdate } from '#/api/sis/elevatorInfo';
|
||||
import {
|
||||
elevatorInfoAdd,
|
||||
elevatorInfoInfo,
|
||||
elevatorInfoUpdate,
|
||||
} from '#/api/sis/elevatorInfo';
|
||||
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||
|
||||
import { modalSchema } from './data';
|
||||
import { communityTree } from '#/api/property/community';
|
||||
import type { DeviceManageForm, DeviceManageQuery } from '#/api/sis/deviceManage/model';
|
||||
import { deviceManageList } from '#/api/sis/deviceManage';
|
||||
|
||||
const emit = defineEmits<{ reload: [] }>();
|
||||
|
||||
@@ -24,11 +30,11 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
// 默认占满两列
|
||||
formItemClass: 'col-span-1',
|
||||
// 默认label宽度 px
|
||||
labelWidth: 100,
|
||||
labelWidth: 200,
|
||||
// 通用配置项 会影响到所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
}
|
||||
},
|
||||
},
|
||||
schema: modalSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -54,7 +60,10 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
return null;
|
||||
}
|
||||
modalApi.modalLoading(true);
|
||||
setupCommunitySelect()
|
||||
// 加载社区树
|
||||
setupCommunitySelect();
|
||||
// 加载未绑定的设备
|
||||
loadDeviceList();
|
||||
const { id } = modalApi.getData() as { id?: number | string };
|
||||
isUpdate.value = !!id;
|
||||
|
||||
@@ -88,6 +97,56 @@ async function handleConfirm() {
|
||||
}
|
||||
}
|
||||
|
||||
let rows: any = [];
|
||||
|
||||
async function loadDeviceList() {
|
||||
if (rows.length === 0) {
|
||||
const params: DeviceManageQuery = {
|
||||
pageNum: 1,
|
||||
pageSize: 500,
|
||||
};
|
||||
const res = await deviceManageList(params);
|
||||
if (res && res.rows && res.rows.length > 0) {
|
||||
rows = res.rows;
|
||||
}
|
||||
}
|
||||
const arr = rows.map((item: DeviceManageForm) => {
|
||||
return {
|
||||
label: item.deviceName,
|
||||
value: item.id,
|
||||
deviceIp: item.deviceIp,
|
||||
deviceId: item.id,
|
||||
};
|
||||
});
|
||||
|
||||
formApi.updateSchema([
|
||||
{
|
||||
componentProps: () => ({
|
||||
options: arr,
|
||||
mode: 'multiple', // 关键属性,启用多选模式
|
||||
onChange: async (value: string, option: any) => {
|
||||
const data = cloneDeep(await formApi.getValues());
|
||||
data.remoteCallElevatorDeviceId = option;
|
||||
formApi.setValues(data);
|
||||
},
|
||||
}),
|
||||
fieldName: 'remoteCallElevatorDeviceId',
|
||||
},
|
||||
{
|
||||
componentProps: () => ({
|
||||
options: arr,
|
||||
allowClear: true,
|
||||
onChange: async (value: string, option: any) => {
|
||||
const data = cloneDeep(await formApi.getValues());
|
||||
data.elevatorControlDeviceId = option;
|
||||
formApi.setValues(data);
|
||||
},
|
||||
}),
|
||||
fieldName: 'elevatorControlDeviceId',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化城市
|
||||
*/
|
||||
@@ -127,7 +186,6 @@ async function setupCommunitySelect() {
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
async function handleClosed() {
|
||||
await formApi.resetForm();
|
||||
resetInitialized();
|
||||
@@ -139,4 +197,3 @@ async function handleClosed() {
|
||||
<BasicForm />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
|
@@ -1,27 +1,11 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { DictEnum } from '@vben/constants';
|
||||
import { getPopupContainer } from '@vben/utils';
|
||||
|
||||
import { queryListByFactoryNo } from '#/api/sis/accessControlDevice';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'libName',
|
||||
label: '人员库名称',
|
||||
},
|
||||
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
getPopupContainer,
|
||||
options: getDictOptions(DictEnum.SIS_LIB_TYPE),
|
||||
},
|
||||
fieldName: 'libType',
|
||||
label: '库类型',
|
||||
label: '库名称',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -42,15 +26,6 @@ export const columns: VxeGridProps['columns'] = [
|
||||
title: '人员库描述',
|
||||
field: 'libDesc',
|
||||
},
|
||||
{
|
||||
title: '库类型',
|
||||
field: 'libType',
|
||||
slots: { default: 'libType' },
|
||||
},
|
||||
/* {
|
||||
title: '业务类型',
|
||||
field: 'busiType',
|
||||
},*/
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
@@ -71,38 +46,14 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '人员库名称',
|
||||
label: '库名称',
|
||||
fieldName: 'libName',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '人员库描述',
|
||||
label: '描述',
|
||||
fieldName: 'libDesc',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '库类型',
|
||||
fieldName: 'libType',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
getPopupContainer,
|
||||
options: getDictOptions(DictEnum.SIS_LIB_TYPE),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '设备',
|
||||
fieldName: 'eqpIds',
|
||||
component: 'ApiSelect',
|
||||
rules: 'required',
|
||||
componentProps: {
|
||||
mode: 'multiple', // 关键属性,启用多选模式
|
||||
resultField: 'list', // 根据API返回结构调整
|
||||
labelField: 'eqpName',
|
||||
valueField: 'id',
|
||||
api: async () => {
|
||||
return await queryListByFactoryNo('2');
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@@ -1,15 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
|
||||
import type { PersonLibForm } from '#/api/sis/personLib/model';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
|
||||
import {
|
||||
personLibExport,
|
||||
personLibList,
|
||||
@@ -19,6 +17,7 @@ import { commonDownloadExcel } from '#/utils/file/download';
|
||||
|
||||
import { columns, querySchema } from './data';
|
||||
import personLibModal from './personLib-modal.vue';
|
||||
import libAuthModal from '#/views/sis/personLib/libAuth-modal.vue';
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
commonConfig: {
|
||||
@@ -82,6 +81,10 @@ const [PersonLibModal, modalApi] = useVbenModal({
|
||||
connectedComponent: personLibModal,
|
||||
});
|
||||
|
||||
const [LibAuthModal, libAuthModalApi] = useVbenModal({
|
||||
connectedComponent: libAuthModal,
|
||||
});
|
||||
|
||||
function handleAdd() {
|
||||
modalApi.setData({});
|
||||
modalApi.open();
|
||||
@@ -121,6 +124,11 @@ function handleDownloadExcel() {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function handleAuth(row: Required<PersonLibForm>) {
|
||||
libAuthModalApi.setData({ id: row.id });
|
||||
libAuthModalApi.open();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -154,6 +162,8 @@ function handleDownloadExcel() {
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Space>
|
||||
<ghost-button @click.stop="handleAuth(row)"> 授权</ghost-button>
|
||||
|
||||
<ghost-button
|
||||
v-access:code="['sis:personLib:edit']"
|
||||
@click.stop="handleEdit(row)"
|
||||
@@ -183,5 +193,6 @@ function handleDownloadExcel() {
|
||||
</template>
|
||||
</BasicTable>
|
||||
<PersonLibModal @reload="tableApi.query()" />
|
||||
<LibAuthModal @reload="tableApi.query()" />
|
||||
</Page>
|
||||
</template>
|
||||
|
95
apps/web-antd/src/views/sis/personLib/libAuth-modal.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { useBeforeCloseDiff } from '#/utils/popup';
|
||||
import { message, Tree } from 'ant-design-vue';
|
||||
import { queryTree } from '#/api/sis/deviceManage';
|
||||
import { authPersonLib, queryAuthDevice } from '#/api/sis/authRecord';
|
||||
|
||||
const emit = defineEmits<{ reload: [] }>();
|
||||
const title = ref('门禁授权');
|
||||
const { onBeforeClose } = useBeforeCloseDiff({});
|
||||
const [BasicModal, modalApi] = useVbenModal({
|
||||
// 在这里更改宽度
|
||||
class: 'w-[700px]',
|
||||
fullscreenButton: false,
|
||||
onBeforeClose,
|
||||
onClosed: handleClosed,
|
||||
onConfirm: handleConfirm,
|
||||
onOpenChange: async (isOpen) => {
|
||||
if (!isOpen) {
|
||||
const { id } = modalApi.getData();
|
||||
queryAuthDevice(id).then((res = []) => {
|
||||
const arr: any[] = [];
|
||||
res.forEach((item) => {
|
||||
arr.push(item.deviceId);
|
||||
checkedKeys.value = arr;
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
modalApi.lock(true);
|
||||
const { id } = modalApi.getData();
|
||||
const arr = checkedKeys.value;
|
||||
if (!arr || arr.length == 0) {
|
||||
message.error('请选择授权设备');
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
libId: id,
|
||||
deviceIds: arr,
|
||||
};
|
||||
authPersonLib(params);
|
||||
emit('reload');
|
||||
modalApi.close();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
// await formApi.resetForm();
|
||||
// resetInitialized();
|
||||
}
|
||||
|
||||
const checkedKeys = ref<any[]>([]);
|
||||
const treeData = ref([]);
|
||||
|
||||
const fieldNames = {
|
||||
title: 'label',
|
||||
key: 'code',
|
||||
children: 'children',
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadDeviceTree();
|
||||
});
|
||||
|
||||
function loadDeviceTree() {
|
||||
queryTree().then((data: any) => {
|
||||
treeData.value = data;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal :title="title">
|
||||
<div class="p-4">
|
||||
<Tree
|
||||
defaultExpandAll
|
||||
:fieldNames="fieldNames"
|
||||
v-model:checkedKeys="checkedKeys"
|
||||
checkable
|
||||
:tree-data="treeData"
|
||||
>
|
||||
</Tree>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
@@ -7,7 +7,6 @@ import { getPopupContainer } from '@vben/utils';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
import { personLibList } from '#/api/sis/personLib';
|
||||
import type { PersonLibQuery, PersonLibVO } from '#/api/sis/personLib/model';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
let libArr: PersonLibVO[] = [];
|
||||
const labelText: VbenFormSchema = {
|
||||
@@ -16,7 +15,7 @@ const labelText: VbenFormSchema = {
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
resultField: 'list', // 根据API返回结构调整
|
||||
labelField: 'labelText',
|
||||
labelField: 'libName',
|
||||
valueField: 'id',
|
||||
// immediate: true,
|
||||
api: async () => {
|
||||
@@ -26,19 +25,6 @@ const labelText: VbenFormSchema = {
|
||||
pageSize: 500,
|
||||
};
|
||||
const res = await personLibList(params);
|
||||
res.rows.forEach((item) => {
|
||||
let tag =
|
||||
item.libType == 1 ? (
|
||||
<Tag color="#108ee9">人像库</Tag>
|
||||
) : (
|
||||
<Tag color="#2db7f5">工服库</Tag>
|
||||
);
|
||||
item.labelText = (
|
||||
<span>
|
||||
{item.libName} {tag}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
libArr = res.rows;
|
||||
}
|
||||
return libArr;
|
||||
@@ -157,7 +143,7 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
},
|
||||
{
|
||||
label: '人像图片',
|
||||
fieldName: 'imgUrl',
|
||||
fieldName: 'imgOssId',
|
||||
component: 'ImageUpload',
|
||||
componentProps: {
|
||||
// accept: 'image/*', // 可选拓展名或者mime类型 ,拼接
|
||||
|
@@ -27,7 +27,7 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
// 默认占满两列
|
||||
formItemClass: 'col-span-1',
|
||||
// 默认label宽度 px
|
||||
labelWidth: 80,
|
||||
labelWidth: 100,
|
||||
// 通用配置项 会影响到所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
|
@@ -27,7 +27,8 @@ export default defineConfig(async () => {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// mock代理目标地址
|
||||
target: 'http://192.168.1.101:8080',
|
||||
// target: 'http://192.168.43.169:8080',
|
||||
target: 'http://127.0.0.1:8080',
|
||||
// target: 'http://192.168.0.108:8080',
|
||||
// target: 'http://192.168.0.106:8080',
|
||||
// target: 'http://47.109.37.87:3010',
|
||||
|