yangg 14 小時之前
父節點
當前提交
93dd693ce4

+ 36 - 2
src/views/system/device/crud.tsx

@@ -362,8 +362,24 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 							minWidth: 100,
 							formatter: ({ row }) => {
 								const isBorrowed = row.borrowed_quantity > 0;
-								const label = isBorrowed ? row.total_quantity-row.borrowed_quantity==0?'在借':'在借' : row.total_quantity==0?'在借':'空闲';
-								const color = isBorrowed ? '#FFA500' : '#4CAF50';
+								const isMaintenance = row.maintenance_quantity > 0 && row.maintenance_quantity === row.total_quantity;
+								const isDamaged = row.damaged_quantity > 0 && row.damaged_quantity === row.total_quantity;
+								
+								let label, color;
+								
+								if (isDamaged) {
+									label = '报损';
+									color = '#F56C6C';
+								} else if (isMaintenance) {
+									label = '维修';
+									color = '#E6A23C';
+								} else if (isBorrowed) {
+									label = row.total_quantity - row.borrowed_quantity === 0 ? '在借' : '在借';
+									color = '#FFA500';
+								} else {
+									label = row.total_quantity === 0 ? '在借' : '空闲';
+									color = '#4CAF50';
+								}
 								return h(
 									'span',
 									{
@@ -439,6 +455,24 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						rules: [{ required: true, message: '' }],
 					},
 				},
+				available_quantity:{
+					title:'可用库存',
+					type:"input",
+					column: { minWidth: 100 },
+					addForm:{
+						show:false,
+					},
+					viewForm:{
+						show:false,
+					},
+					editForm:{show:false},
+					from:{ 
+						show:false,
+						component: { placeholder: '请输入库存数量' },
+						rules: [{ required: false, message: '请输入库存数量' }],
+
+					}
+				},
 				total_quantity:{
 					title:'库存数量',
 					type:"input",

+ 1 - 1
src/views/system/deviceclass/api.ts

@@ -4,7 +4,7 @@ import { request } from '/@/utils/service';
 export const apiPrefix = '/api/system/device/category/';
 export function GetList(query: UserPageQuery) {
 	return request({
-		url: apiPrefix,
+		url: '/api/system/device/category/all_with_disabled/',
 		method: 'get',
 		params: query,
 	});

+ 3 - 3
src/views/system/deviceclass/crud.tsx

@@ -109,7 +109,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					treeNode: true,
 					type: 'input',
 					column: {
-						minWidth: 120,
+						
 					},
 					form: {
 						rules: [
@@ -129,7 +129,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					title: '设备描述',
 					type: 'input',
 					column: {
-						width: 900,
+						width: 800,
 					},
 					form: {
 						component: { placeholder: '请填写设备描述' },
@@ -179,7 +179,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					title: '状态',
 					type: 'dict-select',
 					column: {
-						minWidth: 120,
+						width: 120,
 					},
 					dict: dict({
 						data: [

+ 68 - 20
src/views/system/devicedamage/crud.tsx

@@ -1,4 +1,4 @@
-import { AddReq, CreateCrudOptionsProps,dict, CreateCrudOptionsRet, UserPageQuery,compute } from '@fast-crud/fast-crud';
+import { AddReq, EditReq, DelReq, CreateCrudOptionsProps,dict, CreateCrudOptionsRet, UserPageQuery,compute } from '@fast-crud/fast-crud';
 import * as api from './api';
 import { auth } from '/@/utils/authFunction';
 import { ja } from 'element-plus/es/locale';
@@ -66,7 +66,9 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						text: '撤销',
 						type: 'danger',
 						order: 98,
-						show: true,
+						show: compute(({ row }) => {
+							return row.status !== 5;
+						}),
 						click({ row }) {
 							ElMessageBox.confirm('确认撤销该报损记录?', '提示', {
 							confirmButtonText: '确定',
@@ -144,7 +146,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					}
 				},
 				device:{
-					title: '设备id',
+					title: '设备名称',
 					type: 'dict-select',
 					column: {
 						show:false,
@@ -156,8 +158,39 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						label: 'name',
 					}),
 					form: {
-						component: { placeholder: '请填写设备id' },
-						rules: [{ required: true, message: '请填写设备id' }],
+						component: { 
+							placeholder: '请填写设备名称',
+							onChange: async (context: any) => {
+								// 当选择设备时,自动回填设备编号和设备名称
+								console.log("onChange context:", context);
+								
+								// 获取选中的设备ID
+								const selectedDeviceId = context;
+								if (!selectedDeviceId) return;
+								
+								try {
+									// 通过API获取设备详情
+									const response = await request({
+										url: `/api/system/device/${selectedDeviceId}/`,
+										method: 'get'
+									});
+									console.log("response::::",response)
+									if (response.code==2000) {
+										const deviceData = response.data;
+										console.log("设备数据:", deviceData);
+										
+										// 使用 crudExpose 来设置表单数据
+										const formData = crudExpose.getFormData();
+										formData.device_code = deviceData.code;
+										formData.device_name = deviceData.name;
+										crudExpose.setFormData(formData);
+									}
+								} catch (error) {
+									console.error("获取设备信息失败:", error);
+								}
+							}
+						},
+						rules: [{ required: true, message: '请填写设备名称' }],
 					},
 					editForm:{
 						component: { disabled: true },
@@ -167,37 +200,41 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						rules: [{ required: true, message: '' }],
 					}
 				},
-				device_code:{
-					title: '设备编码',
+				
+				device_name:{
+					title: '设备名称',
 					type: 'input',
 					column: {
-						show:false,
+						show:true,
 						minWidth: 120,
 					},
 					form: {
-						show:true,
-						component: { placeholder: '请填写设备编码' },
-						rules: [{ required: true, message: '请填写设备编码' }],
-					},
-					editForm:{
-						component: { disabled: true },
+						show:false,
+						component: { placeholder: '请填写设备名称' },
+						rules: [{ required: true, message: '请填写设备名称' }],
 					},
 					viewForm:{
 						component: { placeholder: '' },
 						rules: [{ required: true, message: '' }],
 					}
 				},
-				device_name:{
-					title: '设备名称',
+				device_code:{
+					title: '设备编号',
 					type: 'input',
 					column: {
 						show:true,
 						minWidth: 120,
 					},
 					form: {
-						show:false,
-						component: { placeholder: '请填写设备名称' },
-						rules: [{ required: true, message: '请填写设备名称' }],
+						show:true,
+						component: { 
+							placeholder: '请填写设备编号',
+							/* disabled: true  */ // 新增时也禁用,因为会自动回填
+						},
+						rules: [{ required: true, message: '请填写设备编号' }],
+					},
+					editForm:{
+						component: { disabled: true },
 					},
 					viewForm:{
 						component: { placeholder: '' },
@@ -212,7 +249,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					},
 					form: {
 						component: { placeholder: '请填写报废数量'},
-						rules: [{ required: false, message: '请填写报废数量' }],
+						rules: [{ required: true, message: '请填写报废数量' }],
 					},
 					editForm:{
 						component: { disabled: true },
@@ -303,6 +340,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						minWidth: 120,
 					},
 					form: {
+						show:false,
 						component: { placeholder: '请填写负责人' },
 						rules: [{ required: false, message: '请填写负责人' }],
 					},
@@ -327,6 +365,16 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						rules: [{ required: true, message: '' }],
 					}
 				},
+				create_datetime:{
+					title: '创建时间',
+					type: 'datetime',
+					column: {
+						minWidth: 120,
+					},
+					form: {
+						show:false,
+					}
+				}
 
 
 

+ 221 - 46
src/views/system/devicemaintenance/crud.tsx

@@ -62,7 +62,9 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 						show:true,
 					},
 					edit:{
-						show:true,
+						show:compute(({ row }) => {
+							return row.status !== 3;
+						}),
 					},
 					updateStatus: {
 						text: '修改状态',
@@ -104,7 +106,7 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 				damage_no: {
 					title: '编号',
 					search: {
-						show: true,
+						show: false,
 					},
 					treeNode: true,
 					type: 'input',
@@ -187,14 +189,40 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 					}
 				},
 				quantity:{
-					title: '库存数量',
-					type: 'input',
+					title: '维修数量',
+					type: 'number',
 					column: {
 						minWidth: 120,
 					},
 					form: {
-						component: { placeholder: '请填写库存数量'},
-						rules: [{ required: false, message: '请填写库存数量' }],
+						component: { 
+							placeholder: '请填写维修数量',
+							min: 0,
+							precision: 0
+						},
+						rules: [
+							{ required: false, message: '请填写维修数量' },
+							{ 
+								type: 'number', 
+								min: 0, 
+								message: '维修数量不能为负数',
+								trigger: 'blur'
+							},
+							{
+								validator: (rule: any, value: any, callback: any) => {
+									if (value !== null && value !== undefined && value !== '') {
+										if (!Number.isInteger(Number(value)) || Number(value) < 0) {
+											callback(new Error('维修数量必须为正整数'));
+										} else {
+											callback();
+										}
+									} else {
+										callback();
+									}
+								},
+								trigger: 'blur'
+							}
+						],
 					},
 					editForm:{
 						component: { disabled: true },
@@ -217,6 +245,7 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 						],
 					}),
 					column: {
+						show:false,
 						minWidth: 120,
 					},
 					form: {
@@ -233,24 +262,10 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 						rules: [{ required: true, message: '' }],
 					}
 				},
-				damage_reason:{
-					title: '维修原因',
-					type: 'input',
-					column: {
-						minWidth: 120,
-					},
-					form: {
-						component: { placeholder: '请填写维修原因' },
-						rules: [{ required: false, message: '请填写维修原因' }],
-					},
-					viewForm:{
-						component: { placeholder: '' },
-						rules: [{ required: true, message: '' }],
-					}
-				},
+				
 				estimated_loss:{
 					title: '维修费用(元)',
-					type: 'input',
+					type: 'number',
 					column: {
 						minWidth: 120,
 						show:compute(({ form }) => {
@@ -259,19 +274,81 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 						})
 					},
 					form: {
-						component: { placeholder: '请填写维修费用' },
-						rules: [{ required: false, message: '请填写维修费用' }],
+						show:false,
+						component: { 
+							placeholder: '请填写维修费用',
+							min: 0,
+							precision: 2
+						},
+						rules: [
+							{ required: false, message: '请填写维修费用' },
+							{ 
+								type: 'number', 
+								min: 0, 
+								message: '维修费用不能为负数',
+								trigger: 'blur'
+							},
+							{
+								validator: (rule: any, value: any, callback: any) => {
+									if (value !== null && value !== undefined && value !== '') {
+										const numValue = Number(value);
+										if (isNaN(numValue) || numValue < 0) {
+											callback(new Error('维修费用必须为有效的正数'));
+										} else if (numValue > 999999.99) {
+											callback(new Error('维修费用不能超过999999.99元'));
+										} else {
+											callback();
+										}
+									} else {
+										callback();
+									}
+								},
+								trigger: 'blur'
+							}
+						],
 						show:compute(({ form }) => {
 							// 只有当选择了胜任力标签时才显示配置
 							return form && form.damage_type === 1;
 						})
 					},
 					editForm:{
-						show:true,
-						component: { placeholder: '请填写维修费用' },
-						rules: [{ required: compute(({ form }) => {
-							return form && form.status === 3;
-						}), message: '请填写维修费用' }],
+						show:compute(({ form }) => {
+							// 只有当选择了胜任力标签时才显示配置
+							return form && form.status == 3;
+						}),
+						component: { 
+							placeholder: '请填写维修费用',
+							min: 0,
+							precision: 2
+						},
+						rules: [
+							{ required: compute(({ form }) => {
+								return form && form.status === 3;
+							}), message: '请填写维修费用' },
+							{ 
+								type: 'number', 
+								min: 0, 
+								message: '维修费用不能为负数',
+								trigger: 'blur'
+							},
+							{
+								validator: (rule: any, value: any, callback: any) => {
+									if (value !== null && value !== undefined && value !== '') {
+										const numValue = Number(value);
+										if (isNaN(numValue) || numValue < 0) {
+											callback(new Error('维修费用必须为有效的正数'));
+										} else if (numValue > 999999.99) {
+											callback(new Error('维修费用不能超过999999.99元'));
+										} else {
+											callback();
+										}
+									} else {
+										callback();
+									}
+								},
+								trigger: 'blur'
+							}
+						],
 					},
 					addForm:{
 						show:false,
@@ -283,6 +360,9 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 							// 只有当选择了胜任力标签时才显示配置
 							return form && form.damage_type === 1;
 						})
+					},
+					valueResolve({ form, value }) {
+						form.estimated_loss = Number(value);
 					}
 				},
 				responsible_person:{
@@ -292,15 +372,55 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 						minWidth: 120,
 					},
 					form: {
-						component: { placeholder: '请填写维修人' },
-						rules: [{ required: false, message: '请填写维修人' }],
+						show:false,
+						component: { placeholder: '请填写维修人姓名' },
+						rules: [
+							{ required: false, message: '请填写维修人' },
+							{ min: 2, max: 20, message: '维修人姓名长度应在2-20个字符之间', trigger: 'blur' },
+							{
+								validator: (rule: any, value: any, callback: any) => {
+									if (value && value.trim()) {
+										const nameRegex = /^[\u4e00-\u9fa5a-zA-Z\s]+$/;
+										if (!nameRegex.test(value.trim())) {
+											callback(new Error('维修人姓名只能包含中文、英文字母和空格'));
+										} else {
+											callback();
+										}
+									} else {
+										callback();
+									}
+								},
+								trigger: 'blur'
+							}
+						],
 					},
 					editForm:{
-						show:true,
-						component: { placeholder: '请填写维修人' },
-						rules: [{ required: compute(({ form }) => {
-							return form && form.status === 2;
-						}), message: '请填写维修人' }],
+						show:compute(({ form }) => {
+							// 只有当选择了胜任力标签时才显示配置
+							return form && form.status == 3;
+						}),
+						component: { placeholder: '请填写维修人姓名' },
+						rules: [
+							{ required: compute(({ form }) => {
+								return form && form.status === 2;
+							}), message: '请填写维修人' },
+							{ min: 2, max: 20, message: '维修人姓名长度应在2-20个字符之间', trigger: 'blur' },
+							{
+								validator: (rule: any, value: any, callback: any) => {
+									if (value && value.trim()) {
+										const nameRegex = /^[\u4e00-\u9fa5a-zA-Z\s]+$/;
+										if (!nameRegex.test(value.trim())) {
+											callback(new Error('维修人姓名只能包含中文、英文字母和空格'));
+										} else {
+											callback();
+										}
+									} else {
+										callback();
+									}
+								},
+								trigger: 'blur'
+							}
+						],
 					},
 					viewForm:{
 						component: { placeholder: '' },
@@ -308,23 +428,63 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 					}
 				},
 				responsible_phone:{
-					title: '维修人联系方式',
+					title: '维修人电话',
 					type: 'input',
 					column: {
+						show:true,
 						minWidth: 120,
 					},
 					form: {
-						component: { placeholder: '请填写维修人联系方式' },
-						rules: [{ required: false, message: '请填写维修人联系方式' }],
+						show:false,
+						component: { placeholder: '请填写维修人电话' },
+						rules: [
+							{ required: false, message: '请填写维修人电话' },
+							{
+								validator: (rule: any, value: any, callback: any) => {
+									if (value && value.trim()) {
+										const phoneRegex = /^1[3-9]\d{9}$|^0\d{2,3}-?\d{7,8}$|^400-?\d{3}-?\d{4}$/;
+										if (!phoneRegex.test(value.trim())) {
+											callback(new Error('请输入正确的手机号码或固定电话号码'));
+										} else {
+											callback();
+										}
+									} else {
+										callback();
+									}
+								},
+								trigger: 'blur'
+							}
+						],
 					},
 					editForm:{
-						show:true,
-						component: { placeholder: '请填写维修人联系方式' },
-						rules: [{ required: compute(({ form }) => {
-							return form && form.status === 2;
-						}), message: '请填写维修人联系方式' }],
+						show:compute(({ form }) => {
+							// 只有当选择了胜任力标签时才显示配置
+							return form && form.status == 3;
+						}),
+						component: { placeholder: '请填写维修人电话' },
+						rules: [
+							{ required: compute(({ form }) => {
+								return form && form.status === 2;
+							}), message: '请填写维修人电话' },
+							{
+								validator: (rule: any, value: any, callback: any) => {
+									if (value && value.trim()) {
+										const phoneRegex = /^1[3-9]\d{9}$|^0\d{2,3}-?\d{7,8}$|^400-?\d{3}-?\d{4}$/;
+										if (!phoneRegex.test(value.trim())) {
+											callback(new Error('请输入正确的手机号码或固定电话号码'));
+										} else {
+											callback();
+										}
+									} else {
+										callback();
+									}
+								},
+								trigger: 'blur'
+							}
+						],
 					},
 					viewForm:{
+						show:true,
 						component: { placeholder: '' },
 						rules: [{ required: true, message: '' }],
 					}
@@ -336,7 +496,7 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 						minWidth: 120,
 					},
 					form: {
-						show:false,
+						show:true,
 						component: { placeholder: '请填写负责人' },
 						rules: [{ required: false, message: '请填写负责人' }],
 					},
@@ -410,9 +570,24 @@ export const createCrudOptions = function ({ crudExpose,  dialogId,
 						rules: [{ required: true, message: '' }],
 					}
 				},
+				damage_reason:{
+					title: '维修原因',
+					type: 'textarea',
+					column: {
+						minWidth: 120,
+					},
+					form: {
+						component: { placeholder: '请填写维修原因' },
+						rules: [{ required: false, message: '请填写维修原因' }],
+					},
+					viewForm:{
+						component: { placeholder: '' },
+						rules: [{ required: true, message: '' }],
+					}
+				},
 				repair_result:{
 					title: '维修结果',
-					type: 'input',
+					type: 'textarea',
 					column: {
 						minWidth: 120,
 					},

+ 24 - 24
src/views/system/devicemanual/crud.tsx

@@ -96,7 +96,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					column: {show:false}
 				},
 				device:{
-					title: '设备id',
+					title: '设备编号',
 					search: {
 						show: false,
 					},
@@ -113,8 +113,8 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					}),
 					form: {
 						show:true,
-						component: { placeholder: '请选择设备id' },
-						rules: [{ required: true, message: '请选择设备id' }],
+						component: { placeholder: '请选择设备编号' },
+						rules: [{ required: true, message: '请选择设备编号' }],
 					},
 				},
 				article:{
@@ -133,7 +133,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 
 				},
 				device_code: {
-					title: '设备编',
+					title: '设备编',
 					search: {
 						show: false,
 					},
@@ -146,8 +146,8 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						show:false,
 					},
 				},
-				article_title: {
-					title: '设备标题',
+				article_title: {//article_title
+					title: '文章标题',//'设备名称',
 					search: {
 						show: true,
 					},
@@ -160,10 +160,10 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						show:true,
 						rules: [
 							// 表单校验规则
-							{ required: true, message: '标题必填项' },
+							{ required: true, message: '名称必填项' },
 						],
 						component: {
-							placeholder: '请输入标题',
+							placeholder: '请输入名称',
 						},
 					},
 					viewForm:{
@@ -171,6 +171,22 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						rules: [{ required: true, message: '' }],
 					}
 				},
+				device_name:{
+					title: '设备名称',
+					type: 'input',
+					column: {
+						minWidth: 120,
+					},
+					form: {
+						show:false,
+						component: { placeholder: '请填发布人' },
+						rules: [{ required: false, message: '请填写发布人' }],
+					},
+					viewForm:{
+						component: { placeholder: '' },
+						rules: [{ required: true, message: '' }],
+					}
+				},
 				article_content:{
 					title: '内容',
 					// type: 'textarea',
@@ -217,22 +233,6 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						rules: [{ required: true, message: '' }],
 					}
 				},
-				device_name:{
-					title: '发布人',
-					type: 'input',
-					column: {
-						minWidth: 120,
-					},
-					form: {
-						show:false,
-						component: { placeholder: '请填发布人' },
-						rules: [{ required: false, message: '请填写发布人' }],
-					},
-					viewForm:{
-						component: { placeholder: '' },
-						rules: [{ required: true, message: '' }],
-					}
-				},
 				create_datetime:{
 					title: '发布时间',
 					type: 'input',

+ 7 - 0
src/views/system/devicemanual/index.vue

@@ -23,3 +23,10 @@ onMounted(async () => {
 	crudExpose.doRefresh();
 });
 </script>
+<style scoped>
+:deep(.fs-form-footer-btns){
+	position: absolute;
+	bottom: 10px;
+	right: 30px;
+}
+</style>

+ 452 - 17
src/views/system/home/index.vue

@@ -8,6 +8,9 @@
 					<div class="head">
 						<div class="weather">
 							<span id="showTime" style="font-size: 20px;"></span>
+							<el-button class="fullscreen-btn" type="text" @click="refreshPageData" :disabled="isRefreshing" title="刷新数据">
+								<el-icon :size="20" :class="{ 'refresh-rotating': isRefreshing }"><Refresh /></el-icon>
+							</el-button>
 							<el-button class="fullscreen-btn" type="text" @click="toggleFullScreen">
 								<el-icon><FullScreen v-if="!isFullscreen"/><Aim v-else/></el-icon>
 							</el-button>
@@ -21,11 +24,11 @@
 					<div class="mainbox">
 						<ul class="clearfix">
 							<li>
-								<div v-if="panelSettings.deviceUtilization" class="boxall" :style="{ flex: !panelSettings.deviceRanking ? 3.5 : 2 }">
+								<div v-if="panelSettings.deviceUtilization" class="boxall" :style="{ height: !panelSettings.borrowTrend ? '700px' : '360px' }">
 									<div class="alltitle">设备利用率趋势</div>
 									<div class="navboxall" id="echart5" style="height: 95%;"></div>
 								</div>
-								<div v-if="panelSettings.deviceRanking" class="boxall" :style="{ flex: !panelSettings.deviceUtilization ? 3.5 : 1.5, overflow: 'hidden' }">
+								<div v-if="panelSettings.deviceRanking" class="boxall device-duration-container" :style="{ height: !panelSettings.borrowTrend ? '700px' : '340px' }">
 									<div class="alltitle">单台设备累计借用时长排名</div>
 									<div class="navboxall">
 										<div class="wraptit">
@@ -34,7 +37,7 @@
 											<span  style="flex: 1;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;padding: 0 5px;">设备编号</span>
 											<span style="flex: 0 0 80px;text-align: center;">借用时长</span>
 										</div>
-										<div class="wrap" :style="isFullscreen?'height: 85%':'height: 70%'">
+										<div class="wrap device-duration-list">
 											<ul>
 												<li v-for="(hero, index) in heroData" :key="index" style="width: 100%;padding:5px 0 ;border-bottom: 1px solid #232e63 ;">
 													<div
@@ -105,9 +108,9 @@
 										</div>
 									</div>
 								</div>
-								<div v-if="panelSettings.borrowTrend" class="boxall" :style="{ height: !panelSettings.coreMetrics ? '700px' : '340px' }">
+								<div v-if="panelSettings.borrowTrend" class="boxall borrow-trend-container" :style="getBorrowTrendHeight()">
 									<div class="alltitle">整体设备借用次数趋势</div>
-									<div class="navboxall" id="echart3" style="height: 95%;"></div>
+									<div class="navboxall" id="echart3" :style="getBorrowTrendChartStyle()"></div>
 								</div>
 
 								<!-- <div class="boxall" style="height:350px">
@@ -128,7 +131,7 @@
 										<el-radio-button label="year">今年</el-radio-button>
 									</el-radio-group>
 								</div> -->
-								<div v-if="panelSettings.deviceBorrowRanking" class="boxall" :style="{ height: !panelSettings.activeUsers ? '670px' : '400px', overflow: 'hidden' }">
+								<div v-if="panelSettings.deviceBorrowRanking" class="boxall device-count-container">
 									<div class="alltitle">单台设备累计借用次数排名</div>
 									<div class="navboxall">
 										<div class="wraptit">
@@ -137,7 +140,7 @@
 											<span style="flex: 1;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;padding: 0 5px;">设备编号</span>
 											<span style="flex: 0 0 80px;text-align: center;">借用次数</span>
 										</div>
-										<div class="wrap" :style="isFullscreen?'height: 85%':'height: 60%'">
+										<div class="wrap device-count-list">
 											<ul>
 												<li v-for="(hero, index) in teamRankings" :key="index" style="width: 100%;padding:5px 0 ;border-bottom: 1px solid #232e63 ;">
 													<div
@@ -167,7 +170,7 @@
 								</div>
 
 
-								<div v-if="panelSettings.activeUsers" class="boxall" :style="{ height: !panelSettings.deviceBorrowRanking ? '670px' : '310px', overflow: 'hidden' }">
+								<div v-if="panelSettings.activeUsers" class="boxall active-users-container">
 									<div class="alltitle">活跃用户排名</div>
 									<div class="navboxall">
 										<div class="wraptit">
@@ -176,7 +179,7 @@
 											<span style="flex: 1;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;padding: 0 5px;">学号/工号</span>
 											<span style="flex: 0 0 80px;text-align: center;">总借用次数</span>
 										</div>
-										<div class="wrap" :style="isFullscreen?'height: 85%':'height: 60%'">
+										<div class="wrap active-users-list">
 											<ul>
 												<li v-for="(hero, index) in userList" :key="index" style="width: 100%;padding:5px 0 ;border-bottom: 1px solid #232e63 ;">
 													<div
@@ -238,8 +241,9 @@ import { useRouter } from 'vue-router';
 import { request } from '/@/utils/service';
 import dayjs from 'dayjs';
 import { GetList } from '../devicelabel/api'
-import { FullScreen, Aim, Setting } from '@element-plus/icons-vue'
+import { FullScreen, Aim, Setting, Refresh } from '@element-plus/icons-vue'
 import { Session, Local } from '/@/utils/storage';
+import { ElMessage } from 'element-plus';
 
 
 
@@ -266,12 +270,14 @@ export default defineComponent({
 	name: 'home',
 	components: {
 		FullScreen,
-		Aim
+		Aim,
+		Refresh
 	},
 	setup() {
 
 		const scrollOffset = ref(0)
 		const isFullscreen = ref(false)
+		const isRefreshing = ref(false)
 
 		// 切换全屏
 		const toggleFullScreen = () => {
@@ -329,7 +335,7 @@ export default defineComponent({
 				activeUsers: true,      // 活跃用户
 			},
 			settingsDialogVisible: false,
-			homeOne: [],
+			homeOne: [] as Array<{ num1: string }>,
 			homeThree: [],
 			myCharts: [],
 			charts: {
@@ -339,9 +345,11 @@ export default defineComponent({
 			},
 			apiData: null as ApiData | null,
 
-			heroData: [],
-			userList: [],/* 用户统计列表 */
-			teamRankings: []
+			heroData: [] as any[],
+			userList: [] as any[],/* 用户统计列表 */
+			teamRankings: [] as any[],
+			utilizationData: [] as any[], // 利用率趋势数据
+			borrowTrendData: [] as any[]  // 借用趋势数据
 		});
 		const router = useRouter();
 
@@ -650,6 +658,35 @@ export default defineComponent({
 			}
 		);
 
+		// 监听窗口大小变化和全屏状态变化
+		const handleResize = () => {
+			// 触发响应式更新
+			nextTick(() => {
+				// 重新初始化图表以适应新的尺寸
+				const echart3 = document.getElementById('echart3');
+				if (echart3 && window.echarts) {
+					const chartInstance = window.echarts.getInstanceByDom(echart3);
+					if (chartInstance) {
+						chartInstance.resize();
+					}
+				}
+			});
+		};
+
+		// 监听全屏状态变化
+		watch(isFullscreen, () => {
+			handleResize();
+		});
+
+		// 添加窗口大小变化监听器
+		onMounted(() => {
+			window.addEventListener('resize', handleResize);
+		});
+
+		onUnmounted(() => {
+			window.removeEventListener('resize', handleResize);
+		});
+
 		/* const userList = ref([]) */
 		// 获取API数据
 		const fetchApiData = async () => {
@@ -662,11 +699,13 @@ export default defineComponent({
 					updateZbCharts(res.data);
 				}
 				const borrow_rank = Local.get(`borrow_ranking_filters_${Session.get('userInfo').username}`)
+				const borrow_trend = Local.get(`borrow_trends_filters_${Session.get('userInfo').username}`)
 				const params = {
 					type: borrow_rank.borrowType,//selectedType.value,
 					start_date: borrow_rank.dateRange?.[0],
 					end_date: borrow_rank.dateRange?.[1],
-					tag_id:borrow_rank.deviceTagIds
+					tag_id:borrow_rank.deviceTagIds,
+					period:borrow_trend.selectedRange
 				};
 				console.log(borrow_rank);
 				const drres = await api.GetBorrowRankingComposite(params);
@@ -683,11 +722,13 @@ export default defineComponent({
 				if (ulres.code === 2000) {
 					//clos
 					console.log("ulres:::", ulres)
+					state.utilizationData = ulres.data.trends
 					updateMonthlyUtilizationChart(ulres.data.trends)
 				}
 				const btres = await api.GetBorrowRankingComposite(params);
 				if (btres.code === 2000) {
 					//clos
+					state.borrowTrendData = btres.data.borrow_trends.trends
 					updateBorrowTrendChart(btres.data.borrow_trends.trends)
 				}
 				const acres = await api.GetActiveUsers();
@@ -709,6 +750,34 @@ export default defineComponent({
 
 		};
 
+		// 刷新页面数据
+		const refreshPageData = async () => {
+			if (isRefreshing.value) return; // 防止重复点击
+			
+			isRefreshing.value = true;
+			try {
+				// 重新获取所有数据
+				await fetchApiData();
+				
+				// 重新初始化图表
+				setTimeout(() => {
+					initLineChart();
+					initPieChart();
+					initBarChart();
+					// 重新获取并更新图表数据
+					updateMonthlyUtilizationChart(state.utilizationData || []);
+					updateBorrowTrendChart(state.borrowTrendData || []);
+				}, 100);
+				
+				// 显示成功提示
+				ElMessage.success('数据刷新成功');
+			} catch (error) {
+				console.error('刷新数据失败:', error);
+				ElMessage.error('数据刷新失败,请重试');
+			} finally {
+				isRefreshing.value = false;
+			}
+		};
 
 		const handleQuickRange = (range: string) => {
 			const today = dayjs();
@@ -910,7 +979,7 @@ export default defineComponent({
 			echart3.setOption(option);
 		};
 
-		const formatRange = (s) => {
+		const formatRange = (s: string) => {
 			const m = String(s).match(/^(\d{1,2})-(\d{1,2})至(\d{1,2})-(\d{1,2})$/);
 			if (!m) return s;
 			const [, m1, d1, m2, d2] = m;
@@ -1099,6 +1168,76 @@ export default defineComponent({
 		const settingsDialogVisible = computed(() => state.settingsDialogVisible);
 		const panelSettings = computed(() => state.panelSettings);
 
+		// 计算借用趋势面板高度
+		const getBorrowTrendHeight = () => {
+			const isFullscreen = document.fullscreenElement !== null;
+			const windowHeight = window.innerHeight;
+			const windowWidth = window.innerWidth;
+			
+			// 基础高度计算
+			let baseHeight = 380; // 默认高度
+			
+			if (!panelSettings.value.coreMetrics) {
+				// 如果没有核心概览指标,借用趋势面板可以占用更多空间
+				baseHeight = 700;
+			}
+			
+			// 全屏状态下的高度调整
+			if (isFullscreen) {
+				baseHeight = Math.max(baseHeight, windowHeight * 0.4); // 全屏时至少占40%高度
+			}
+			
+			// 根据屏幕尺寸调整
+			if (windowWidth < 1200) {
+				// 小屏幕时减少高度
+				baseHeight = Math.min(baseHeight, 300);
+			} else if (windowWidth > 1600) {
+				// 大屏幕时可以增加高度
+				baseHeight = Math.min(baseHeight + 100, 800);
+			}
+			
+			// 全屏模式下移除高度限制
+			if (isFullscreen) {
+				return {
+					height: `${baseHeight}px`,
+					minHeight: '300px',
+					maxHeight: 'none'
+				};
+			}
+			
+			return {
+				height: `${baseHeight}px`,
+				minHeight: '300px',
+				maxHeight: '800px'
+			};
+		};
+
+		// 计算借用趋势图表样式
+		const getBorrowTrendChartStyle = () => {
+			const isFullscreen = document.fullscreenElement !== null;
+			const windowHeight = window.innerHeight;
+			
+			let chartHeight = '100%';
+			let marginTop = '30px';
+			
+			// 全屏状态下调整图表样式
+			if (isFullscreen) {
+				chartHeight = '95%';
+				marginTop = '15px';
+			}
+			
+			// 小屏幕时调整
+			if (window.innerWidth < 1200) {
+				chartHeight = '90%';
+				marginTop = '15px';
+			}
+			
+			return {
+				height: chartHeight,
+				marginTop: marginTop
+			};
+		};
+
 		return {
 			homeLineRef,
 			homePieRef,
@@ -1119,7 +1258,9 @@ export default defineComponent({
 			settingsDialogVisible,
 			panelSettings,
 			isFullscreen,
+			isRefreshing,
 			toggleFullScreen,
+			refreshPageData,
 			handleClose,
 			// 打开设置弹窗
 			openSettings: () => {
@@ -1131,6 +1272,10 @@ export default defineComponent({
 				// 可以在这里添加保存到本地存储的逻辑
 				localStorage.setItem('dashboardPanelSettings', JSON.stringify(state.panelSettings));
 			},
+			// 借用趋势面板高度计算方法
+			getBorrowTrendHeight,
+			// 借用趋势图表样式计算方法
+			getBorrowTrendChartStyle,
 		};
 	},
 });
@@ -1255,6 +1400,16 @@ $homeNavLengh: 8;
 			.lol-container {
 				height: 100vh;
 				border-radius: 0;
+				
+				.mainbox {
+					height: calc(100vh - 105px);
+					
+					.borrow-trend-container {
+						height: calc(100vh - 200px) !important;
+						max-height: none !important;
+						flex: 1;
+					}
+				}
 			}
 		}
 	}
@@ -1421,6 +1576,118 @@ $homeNavLengh: 8;
 		}
 	}
 
+	// 排名列表响应式优化
+	@media screen and (max-width: 1600px) {
+		.active-users-container,
+		.device-duration-container,
+		.device-count-container {
+			.navboxall {
+				.wraptit {
+					span {
+						font-size: 12px;
+						
+						&:nth-child(1) {
+							flex: 0 0 40px;
+						}
+						
+						&:nth-child(4) {
+							flex: 0 0 70px;
+						}
+					}
+				}
+				
+				.active-users-list,
+				.device-duration-list,
+				.device-count-list {
+					ul li div {
+						span {
+							font-size: 12px;
+							
+							&:nth-child(1) {
+								flex: 0 0 40px;
+							}
+							
+							&:nth-child(4) {
+								flex: 0 0 70px;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	@media screen and (max-width: 1200px) {
+		.active-users-container,
+		.device-duration-container,
+		.device-count-container {
+			.navboxall {
+				.wraptit {
+					span {
+						font-size: 11px;
+						
+						&:nth-child(1) {
+							flex: 0 0 35px;
+						}
+						
+						&:nth-child(4) {
+							flex: 0 0 60px;
+						}
+					}
+				}
+				
+				.active-users-list,
+				.device-duration-list,
+				.device-count-list {
+					ul li div {
+						span {
+							font-size: 11px;
+							
+							&:nth-child(1) {
+								flex: 0 0 35px;
+							}
+							
+							&:nth-child(4) {
+								flex: 0 0 60px;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		// 借用趋势容器在小屏幕下的优化
+		.borrow-trend-container {
+			.alltitle {
+				font-size: 14px;
+				margin-bottom: 8px;
+			}
+			
+			.navboxall {
+				#echart3 {
+					margin-top: 10px !important;
+				}
+			}
+		}
+	}
+
+	// 借用趋势容器响应式优化
+	@media screen and (max-width: 1600px) {
+		.borrow-trend-container {
+			.alltitle {
+				font-size: 15px;
+			}
+		}
+	}
+
+	@media screen and (min-width: 1600px) {
+		.borrow-trend-container {
+			.alltitle {
+				font-size: 17px;
+			}
+		}
+	}
+
 	.todo-list {
 		padding: 10px 20px;
 		margin-top: 15px;
@@ -1534,6 +1801,20 @@ $homeNavLengh: 8;
 						height: 28px;
 						transition: transform 0.2s ease;
 						}
+					
+					// 刷新图标转动动画
+					.refresh-rotating {
+						animation: rotate 1s linear infinite;
+					}
+					
+					@keyframes rotate {
+						from {
+							transform: rotate(0deg);
+						}
+						to {
+							transform: rotate(360deg);
+						}
+					}
 				}
 			}
 
@@ -1689,6 +1970,160 @@ $homeNavLengh: 8;
 					}
 				}
 
+				// 活跃用户排名容器优化
+				.active-users-container {
+					display: flex;
+					flex-direction: column;
+					min-height: 0; // 允许flex子元素收缩
+					
+					.navboxall {
+						flex: 1;
+						display: flex;
+						flex-direction: column;
+						min-height: 0;
+						
+						.wraptit {
+							flex-shrink: 0; // 标题行不收缩
+						}
+						
+						.active-users-list {
+							flex: 1;
+							min-height: 0;
+							overflow-y: auto;
+							overflow-x: hidden;
+							
+							// 自定义滚动条样式
+							&::-webkit-scrollbar {
+								width: 6px;
+							}
+							
+							&::-webkit-scrollbar-track {
+								background: rgba(255, 255, 255, 0.1);
+								border-radius: 3px;
+							}
+							
+							&::-webkit-scrollbar-thumb {
+								background: rgba(255, 255, 255, 0.3);
+								border-radius: 3px;
+								
+								&:hover {
+									background: rgba(255, 255, 255, 0.5);
+								}
+							}
+						}
+					}
+				}
+
+				// 设备借用时长排名容器优化
+				.device-duration-container {
+					display: flex;
+					flex-direction: column;
+					min-height: 0;
+					
+					.navboxall {
+						flex: 1;
+						display: flex;
+						flex-direction: column;
+						min-height: 0;
+						
+						.wraptit {
+							flex-shrink: 0;
+						}
+						
+						.device-duration-list {
+							flex: 1;
+							min-height: 0;
+							overflow-y: auto;
+							overflow-x: hidden;
+							
+							// 自定义滚动条样式
+							&::-webkit-scrollbar {
+								width: 6px;
+							}
+							
+							&::-webkit-scrollbar-track {
+								background: rgba(255, 255, 255, 0.1);
+								border-radius: 3px;
+							}
+							
+							&::-webkit-scrollbar-thumb {
+								background: rgba(255, 255, 255, 0.3);
+								border-radius: 3px;
+								
+								&:hover {
+									background: rgba(255, 255, 255, 0.5);
+								}
+							}
+						}
+					}
+				}
+
+				// 设备借用次数排名容器优化
+				.device-count-container {
+					display: flex;
+					flex-direction: column;
+					min-height: 0;
+					
+					.navboxall {
+						flex: 1;
+						display: flex;
+						flex-direction: column;
+						min-height: 0;
+						
+						.wraptit {
+							flex-shrink: 0;
+						}
+						
+						.device-count-list {
+							flex: 1;
+							min-height: 0;
+							overflow-y: auto;
+							overflow-x: hidden;
+							
+							// 自定义滚动条样式
+							&::-webkit-scrollbar {
+								width: 6px;
+							}
+							
+							&::-webkit-scrollbar-track {
+								background: rgba(255, 255, 255, 0.1);
+								border-radius: 3px;
+							}
+							
+							&::-webkit-scrollbar-thumb {
+								background: rgba(255, 255, 255, 0.3);
+								border-radius: 3px;
+								
+								&:hover {
+									background: rgba(255, 255, 255, 0.5);
+								}
+							}
+						}
+					}
+				}
+
+				// 借用趋势容器优化
+				.borrow-trend-container {
+					display: flex;
+					flex-direction: column;
+					min-height: 0;
+					transition: height 0.3s ease;
+					
+					.navboxall {
+						flex: 1;
+						display: flex;
+						flex-direction: column;
+						min-height: 0;
+						position: relative;
+						
+						// 确保图表容器能够正确填充
+						#echart3 {
+							width: 100% !important;
+							height: 100% !important;
+						}
+					}
+				}
+
 				.wrap::-webkit-scrollbar {
 					display: none;
 					/* 隐藏滚动条 */

+ 92 - 1
src/views/system/screenconsole/component/BorrowTrendsChart.vue

@@ -41,6 +41,7 @@
     LegendComponent
   } from 'echarts/components'
   import * as api from '../api'
+  import { Session, Local } from '/@/utils/storage'
   
   // use([
   //   CanvasRenderer,
@@ -75,6 +76,9 @@
   const selectedRange = ref('week')
   const rawData = ref<BorrowTrend[]>([])
   
+  // 本地存储相关配置 - 使用不同的键名避免与BorrowRankingList冲突
+  const STORAGE_KEY = 'borrow_trends_filters'
+  
   const fetchData = async () => {
     const res = await api.GetBorrowTrends(selectedRange.value)
     // const res = await api.GetBorrowTrends()
@@ -83,10 +87,97 @@
     } else {
       rawData.value = []
     }
+    
+    // 保存当前筛选条件到本地存储
+    saveFiltersToStorage()
   }
   
+  // 保存筛选条件到本地存储
+  const saveFiltersToStorage = () => {
+    try {
+      const userInfo = Session.get('userInfo')
+      if (!userInfo?.username) return
+      
+      const filters = {
+        selectedRange: selectedRange.value,
+        timestamp: Date.now(), // 添加时间戳用于数据有效期控制
+        version: '1.0', // 版本号用于后续数据结构升级
+        component: 'BorrowTrendsChart' // 标识组件来源,便于调试和维护
+      }
+      
+      // 使用用户名作为存储键的一部分,实现用户级别的数据隔离
+      const storageKey = `${STORAGE_KEY}_${userInfo.username}`
+      Local.set(storageKey, filters)
+      
+      console.log('BorrowTrendsChart筛选条件已保存到本地存储', filters)
+    } catch (error) {
+      console.error('保存BorrowTrendsChart筛选条件失败:', error)
+    }
+  }
+
+  // 从本地存储恢复筛选条件
+  const loadFiltersFromStorage = () => {
+    try {
+      const userInfo = Session.get('userInfo')
+      if (!userInfo?.username) return
+      
+      const storageKey = `${STORAGE_KEY}_${userInfo.username}`
+      const storedData = Local.get(storageKey)
+      
+      if (!storedData) return
+      
+      // 检查数据有效期(7天)
+      const now = Date.now()
+      const maxAge = 7 * 24 * 60 * 60 * 1000 // 7天
+      if (storedData.timestamp && (now - storedData.timestamp) > maxAge) {
+        Local.remove(storageKey)
+        console.log('BorrowTrendsChart筛选条件已过期,已清除本地数据')
+        return
+      }
+      
+      // 验证数据来源,确保是BorrowTrendsChart组件的数据
+      if (storedData.component && storedData.component !== 'BorrowTrendsChart') {
+        console.warn('检测到非BorrowTrendsChart组件的数据,跳过恢复')
+        return
+      }
+      
+      // 恢复筛选条件
+      if (storedData.selectedRange && ['week', 'month', 'year'].includes(storedData.selectedRange)) {
+        selectedRange.value = storedData.selectedRange
+        console.log('BorrowTrendsChart筛选条件已从本地存储恢复:', storedData.selectedRange)
+      }
+    } catch (error) {
+      console.error('恢复BorrowTrendsChart筛选条件失败:', error)
+      // 发生错误时清除可能损坏的数据
+      const userInfo = Session.get('userInfo')
+      if (userInfo?.username) {
+        const storageKey = `${STORAGE_KEY}_${userInfo.username}`
+        Local.remove(storageKey)
+      }
+    }
+  }
+
+  // 清除本地存储的筛选条件
+  const clearStoredFilters = () => {
+    try {
+      const userInfo = Session.get('userInfo')
+      if (!userInfo?.username) return
+      
+      const storageKey = `${STORAGE_KEY}_${userInfo.username}`
+      Local.remove(storageKey)
+      console.log('已清除BorrowTrendsChart本地存储的筛选条件')
+    } catch (error) {
+      console.error('清除BorrowTrendsChart筛选条件失败:', error)
+    }
+  }
+
   watch(selectedRange, fetchData)
-  onMounted(fetchData)
+  
+  onMounted(() => {
+    // 先恢复筛选条件,再获取数据
+    loadFiltersFromStorage()
+    fetchData()
+  })
   
   const hasData = computed(() => rawData.value.length > 0)
 

+ 13 - 3
src/views/system/suppliermanage/crud.tsx

@@ -299,7 +299,17 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						component: { placeholder: '' },
 						rules: [{ required: true, message: '' }],
 					}
-				},		
+				},	
+				create_datetime:{
+					title: '创建时间',
+					type: 'datetime',
+					column: {
+						minWidth: 120,
+					},
+					form: {
+						show:false,
+					}
+				},	
 				status: {
 					title: '状态',
 					type: 'dict-select',
@@ -323,10 +333,10 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						rules: [{ required: true, message: '' }],
 					}
 				},
-				...commonCrudConfig({
+				/* ...commonCrudConfig({
 					create_datetime: { search: false },
 					update_datetime: { search: false },
-				}),
+				}), */
 			},
 		},
 	};