From 9715c8755d66b004eaee134e948b9d19c2be0981 Mon Sep 17 00:00:00 2001
From: fengerli <528575642@qq.com>
Date: Sat, 5 Jul 2025 14:30:12 +0800
Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=AE=A1=E7=90=86=EF=BC=8C?=
=?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=88=97=E8=A1=A8=EF=BC=8C=E5=AE=A2=E6=88=B7?=
=?UTF-8?q?=E5=88=97=E8=A1=A8=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=96=84=E6=8F=90?=
=?UTF-8?q?=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env.development | 2 +-
.env.production | 2 +-
index.html | 2 +-
package.json | 5 +-
public/favicon.ico | Bin 8131 -> 0 bytes
public/favicon.png | Bin 0 -> 19464 bytes
src/api/equipmentManagement/device/index.ts | 58 +-
src/assets/images/avatar.png | Bin 0 -> 1865 bytes
src/assets/images/下载.png | Bin 0 -> 19464 bytes
src/assets/logo/logo.png | Bin 8131 -> 19464 bytes
src/layout/components/Navbar.vue | 45 +-
src/layout/components/Sidebar/Logo.vue | 2 +-
src/main.ts | 9 +-
src/plugins/modal.ts | 2 +-
src/utils/auth.ts | 5 +
src/utils/request.ts | 38 +-
src/views/customerManagement/index.vue | 17 +-
.../equipmentManagement/deviceType/index.vue | 8 +-
.../equipmentManagement/devices/index.vue | 581 ++++++++++++++----
src/views/index.vue | 142 -----
src/views/login.vue | 39 +-
src/views/system/role/index.vue | 2 +-
22 files changed, 633 insertions(+), 326 deletions(-)
delete mode 100644 public/favicon.ico
create mode 100644 public/favicon.png
create mode 100644 src/assets/images/avatar.png
create mode 100644 src/assets/images/下载.png
diff --git a/.env.development b/.env.development
index 002785a..b492dee 100644
--- a/.env.development
+++ b/.env.development
@@ -1,5 +1,5 @@
# 页面标题
-VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统11
+VITE_APP_TITLE = 物联网管理系统
# 开发环境配置
VITE_APP_ENV = 'development'
diff --git a/.env.production b/.env.production
index 1109bc6..c5b65c9 100644
--- a/.env.production
+++ b/.env.production
@@ -1,5 +1,5 @@
# 页面标题
-VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
+VITE_APP_TITLE = 物联网管理系统
# 生产环境配置
VITE_APP_ENV = 'production'
diff --git a/index.html b/index.html
index aa1c86d..47ca3f6 100644
--- a/index.html
+++ b/index.html
@@ -6,7 +6,7 @@
-
RuoYi-Vue-Plus多租户管理系统
+ 物联网管理系统
+
+ {{ useUserStore().roles[0] }}
@@ -109,6 +105,7 @@ const newNotice = ref(0);
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userId = ref(userStore.userId);
+console.log(useUserStore().roles[0], 'userStoreuserStoreuserStore');
const companyName = ref(undefined);
@@ -287,21 +284,25 @@ watch(
.avatar-wrapper {
margin-top: 5px;
position: relative;
+ display: flex;
+ align-items: center;
.user-avatar {
cursor: pointer;
- width: 40px;
- height: 40px;
+ width: 20px;
+ height: 20px;
border-radius: 10px;
- margin-top: 10px;
+ display: flex;
+ // margin-top: 10px;
}
i {
+
cursor: pointer;
position: absolute;
- right: -20px;
- top: 25px;
- font-size: 12px;
+ right: -28px;
+ top: -2px;
+ font-size: 21px;
}
}
}
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index 511d788..f51b3e1 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -34,7 +34,7 @@ defineProps({
}
});
-const title = ref('RuoYi-Vue-Plus');
+const title = ref('物联网管理系统');
const settingsStore = useSettingsStore();
const sideTheme = computed(() => settingsStore.sideTheme);
diff --git a/src/main.ts b/src/main.ts
index 63a5c35..791b2d2 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -11,7 +11,9 @@ import router from './router';
// 自定义指令
import directive from './directive';
-
+//引入mitt
+import mitt, { Emitter } from "mitt";
+const mitter = mitt();
// 注册插件
import plugins from './plugins/index'; // plugins
@@ -43,7 +45,10 @@ import { ElDialog } from 'element-plus';
ElDialog.props.closeOnClickModal.default = false;
const app = createApp(App);
-
+//mit事件总线
+app.config.globalProperties.$mitt = mitter;
+// 不需使用 getCurrentInstance 获取实例
+app.provide("$mitt", mitter);
app.use(HighLight);
app.use(ElementIcons);
app.use(router);
diff --git a/src/plugins/modal.ts b/src/plugins/modal.ts
index a8b0548..f4d660e 100644
--- a/src/plugins/modal.ts
+++ b/src/plugins/modal.ts
@@ -51,7 +51,7 @@ export default {
ElNotification.warning(content);
},
// 确认窗体
- confirm(content: any): Promise {
+ confirm(content: any, p0: string, p1: { confirmButtonText: string; cancelButtonText: string; type: string; }): Promise {
return ElMessageBox.confirm(content, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
diff --git a/src/utils/auth.ts b/src/utils/auth.ts
index db50ac9..2ebd7fe 100644
--- a/src/utils/auth.ts
+++ b/src/utils/auth.ts
@@ -3,6 +3,11 @@ const TokenKey = 'Admin-Token';
const tokenStorage = useStorage(TokenKey, null);
export const getToken = () => tokenStorage.value;
+//添加token特殊处理,没有应用到全局
+export const getBearerToken = () => ({
+ Authorization: tokenStorage.value ? `Bearer ${tokenStorage.value}` : undefined,
+ clientid: import.meta.env.VITE_APP_CLIENT_ID,
+});
export const setToken = (access_token: string) => (tokenStorage.value = access_token);
diff --git a/src/utils/request.ts b/src/utils/request.ts
index f3b06ad..1f6ff93 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -146,7 +146,7 @@ service.interceptors.response.use(
isRelogin.show = false;
});
}
- return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
+ return Promise.reject('token已过期,请重新登录。');
} else if (code === HttpStatus.SERVER_ERROR) {
ElMessage({ message: msg, type: 'error' });
return Promise.reject(new Error(msg));
@@ -173,19 +173,28 @@ service.interceptors.response.use(
return Promise.reject(error);
}
);
-// 通用下载方法
-export function download(url: string, params: any, fileName: string) {
- downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
- // prettier-ignore
- return service.post(url, params, {
- transformRequest: [
- (params: any) => {
- return tansParams(params);
- }
- ],
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
- responseType: 'blob'
- }).then(async (resp: any) => {
+// 通用下载方法 get 跟post下载方式
+export function download(url: string, params: any, fileName: string, method: 'get' | 'post' = 'post') {
+ downloadLoadingInstance = ElLoading.service({
+ text: '正在下载数据,请稍候',
+ background: 'rgba(0, 0, 0, 0.7)'
+ });
+ // 请求配置
+ const config = {
+ method,
+ responseType: 'blob',
+ [method === 'get' ? 'params' : 'data']: method === 'get' ? params : tansParams(params),
+ headers: {
+ 'Content-Type': method === 'get'
+ ? 'application/json'
+ : 'application/x-www-form-urlencoded'
+ }
+ };
+ return service.request({
+ url,
+ ...config
+ })
+ .then(async (resp: any) => {
const isLogin = blobValidate(resp);
if (isLogin) {
const blob = new Blob([resp]);
@@ -203,6 +212,7 @@ export function download(url: string, params: any, fileName: string) {
ElMessage.error('下载文件出现错误,请联系管理员!');
downloadLoadingInstance.close();
});
+
}
// 导出 axios 实例
export default service;
diff --git a/src/views/customerManagement/index.vue b/src/views/customerManagement/index.vue
index 264b0d2..ac5f50c 100644
--- a/src/views/customerManagement/index.vue
+++ b/src/views/customerManagement/index.vue
@@ -91,7 +91,7 @@
-
+
@@ -105,14 +105,14 @@
-
+
@@ -137,7 +137,7 @@ const initPassword = ref('');
const queryFormRef = ref();
const userFormRef = ref();
const formDialogRef = ref();
-
+const loadingIng = ref(false)
const dialog = reactive({
visible: false,
title: ''
@@ -165,6 +165,12 @@ const initData: PageData = {
],
userName: [
{ required: true, message: '请输入客户账号', trigger: 'blur' },
+ {
+ min: 2,
+ max: 20,
+ message: '客户账号长度必须在 2 和 20 之间',
+ trigger: 'blur'
+ }
],
password: [
{ required: true, message: '请输入账号密码', trigger: 'blur' },
@@ -285,12 +291,15 @@ const handleUpdate = async (row?: UserForm) => {
/** 提交按钮 */
const submitForm = () => {
+
userFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
+ loadingIng.value = true
form.value.customerId ? await api.updateCustomer(form.value) : await api.addCustomer(form.value);
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
+ loadingIng.value = false
}
});
};
diff --git a/src/views/equipmentManagement/deviceType/index.vue b/src/views/equipmentManagement/deviceType/index.vue
index 4ef64bb..5174679 100644
--- a/src/views/equipmentManagement/deviceType/index.vue
+++ b/src/views/equipmentManagement/deviceType/index.vue
@@ -126,7 +126,7 @@
@@ -151,7 +151,7 @@ const initPassword = ref('');
const queryFormRef = ref();
const userFormRef = ref();
const formDialogRef = ref();
-
+const loadingIng = ref(false)
const dialog = reactive({
visible: false,
title: ''
@@ -224,7 +224,7 @@ const handleDelete = async (row?: UserVO) => {
return;
}
// 单行删除逻辑
- const [err] = await to(proxy?.$modal.confirm('是否确认删除"' + row.nickName + '"的数据项?') as any);
+ const [err] = await to(proxy?.$modal.confirm('是否确认删除"' + row.typeName + '"的数据项?') as any);
if (!err) {
await api.deleteDeviceType([row.id]);
await getList();
@@ -281,10 +281,12 @@ const handleUpdate = async (row?: UserForm) => {
const submitForm = () => {
userFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
+ loadingIng.value = true;
form.value.id ? await api.updateDeviceType(form.value) : await api.addDeviceType(form.value);
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
+ loadingIng.value = false;
}
});
};
diff --git a/src/views/equipmentManagement/devices/index.vue b/src/views/equipmentManagement/devices/index.vue
index 2d0aa84..dbcaf92 100644
--- a/src/views/equipmentManagement/devices/index.vue
+++ b/src/views/equipmentManagement/devices/index.vue
@@ -14,19 +14,19 @@
-
+
-
+
-
+
@@ -51,9 +51,23 @@
修改
+
-
- 删除
+ 导出
+
+
+
+ 批量删除
+
+
+
+
+ 批量导入
+
+
+
+
+ 批量分配客户
@@ -64,11 +78,14 @@
-
+
-
-
-
+
+
+
+
+
@@ -90,10 +107,10 @@
-
+
-
+
@@ -101,6 +118,17 @@
+
+
+
+
+
+
+
+
+ 解绑
@@ -122,22 +150,23 @@
-
-
+
+ handleDeviceTypeChange(id)"
+ :disabled="form.id != ''">
-
+
-
+
@@ -151,9 +180,9 @@
:http-request="httpRequestImg" :file-list="fileList" :limit="1">
-
+
-
+
@@ -168,11 +197,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
请按照模板文件的格式准备需要导入的数据。
+
模板文件中的表头请勿修改,数据请从第二行开始填写。
+
下载模板文件
+
+
+ 选择文件开始导入
+
+
+
批量导入完成,共检测到 {{ importResult.total }}
+ 条数据,导入成功 {{ importResult.succeed }} 条,失败 {{
+ importResult.errorSun }} 条。
+
>>>
+ 上传失败明细下载
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -182,6 +273,8 @@ import { deviceForm, deviceQuery, deviceVO } from '@/api/equipmentManagement/dev
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const deviceDist = ref();
import { to } from 'await-to-js';
+import request from "@/utils/request";
+import { getBearerToken } from '@/utils/auth'
const loading = ref(true);
const showSearch = ref(true);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
@@ -194,7 +287,17 @@ const queryFormRef = ref();
const userFormRef = ref();
const formDialogRef = ref();
const deviceTypeOptions = ref([]); //设备类型
-
+const fileList = ref()
+const communicationModeInfo = ref(null);
+const showMacField = ref(false); //MAC地址
+const showImeiField = ref(false); //mei地址
+const assignDialogVisible = ref(false); //分配客户
+const importDialogVisible = ref(false);//批量导入
+const batchAssignDialogVisible = ref(false) //批量分配客户
+const loadingIng = ref(false)
+const assignCustomerId = ref(); //分配客户id
+const batchAssignCustomerId = ref() //批量分配客户id
+const customerList = ref()
const dialog = reactive({
visible: false,
title: ''
@@ -207,7 +310,7 @@ const initFormData: deviceForm = {
remark: '',
id: '',
deviceType: "",
- image:''
+ image: ''
};
const initData: PageData = {
@@ -229,12 +332,6 @@ const initData: PageData = {
deviceType: [
{ required: true, message: '请选择设备类型', trigger: 'blur' },
],
- deviceMac: [
- { required: true, message: '请输入设备MAC', trigger: 'blur' },
- ],
- deviceImei: [
- { required: true, message: '请输入设备IMEI', trigger: 'blur' },
- ],
}
};
const data = reactive>(initData);
@@ -256,9 +353,9 @@ const handleQuery = () => {
};
/** 重置按钮操作 */
const resetQuery = () => {
- queryParams.value.pageNum = 1;
- getList();
-
+ queryFormRef.value?.resetFields();
+ dateRange.value = ['', ''];
+ handleQuery()
};
/** 删除按钮操作 */
@@ -275,13 +372,63 @@ const handleDelete = async (row?: deviceVO) => {
return;
}
// 单行删除逻辑
- const [err] = await to(proxy?.$modal.confirm('是否确认删除"' + row.nickName + '"的数据项?') as any);
+ const [err] = await to(proxy?.$modal.confirm('是否确认删除"' + row.deviceName + '"的数据项?') as any);
if (!err) {
await api.deleteDevice([row.id]);
await getList();
proxy?.$modal.msgSuccess('删除成功');
}
};
+/** 导出按钮操作 */
+const handleExport = () => {
+ proxy?.download(
+ '/api/device/download',
+ {
+ ...queryParams.value
+ },
+ `${new Date().getTime()}.xlsx`,
+ 'get'
+ );
+};
+// 解绑
+const handleUnbind = (row) => {
+ proxy?.$modal.confirm(`确定要解绑设备 ${row.deviceName} 吗?`, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ api.deviceUnbind(row).then(res => {
+ if (res.code === 0) {
+ proxy?.$modal.msgSuccess('解绑成功')
+ getList(); // 初始化列表数据
+ } else {
+ proxy?.$modal.msgError(res.msg || '解绑失败')
+ }
+ })
+ }).catch(() => {
+
+ })
+};
+// 撤回
+const handleWithdraw = (row: any) => {
+ proxy?.$modal.confirm(`确定要从客户 ${row.customerName} 撤回设备 ${row.deviceName} 吗?`, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ api.withdrawDevice([row.id]).then(res => {
+ if (res.code === 0) {
+ proxy?.$modal.msgSuccess('撤回成功')
+ getList(); // 初始化列表数据
+ } else {
+ proxy?.$modal.msgError(res.msg || '撤回失败')
+ }
+ })
+ }).catch(() => {
+ proxy?.$modal.msgError('已取消撤回')
+ })
+}
+
/** 选择条数 */
const handleSelectionChange = (selection: deviceVO[]) => {
@@ -295,6 +442,7 @@ const handleSelectionChange = (selection: deviceVO[]) => {
const reset = () => {
form.value = { ...initFormData };
userFormRef.value?.resetFields();
+ fileList.value = []
};
/** 取消按钮 */
const cancel = () => {
@@ -308,6 +456,9 @@ const handleAdd = async () => {
dialog.visible = true;
dialog.title = '新增设备';
form.value.password = initPassword.value.toString();
+ // 新增时默认不显示
+ showMacField.value = false;
+ showImeiField.value = false;
};
/** 修改按钮操作 */
@@ -317,93 +468,163 @@ const handleUpdate = async (row?: deviceForm) => {
dialog.title = '修改设备';
try {
if (row) {
- // 从行内按钮调用,直接使用行数据
Object.assign(form.value, row);
+ form.value.image = row.devicePic
+ // 编辑时根据已有值显示字段
+ showMacField.value = !!row.deviceMac;
+ showImeiField.value = !!row.deviceImei;
} else {
const customerId = ids.value[0];
Object.assign(form.value, customerId);
+ form.value.image = customerId.devicePic //图片回显
+ // 编辑时根据已有值显示字段
+ showMacField.value = !!customerId.deviceMac;
+ showImeiField.value = !!customerId.deviceImei;
+ }
+ // 加载设备类型对应的通讯方式
+ if (form.value.deviceType) {
+ await handleDeviceTypeChange(form.value.deviceType);
}
} catch (error) {
dialog.visible = false;
}
};
- // 覆盖默认的上传行为,可以自定义上传的实现
- const httpRequestImg=(parm)=> { };
- const beforeUpload=(file)=> {
- const isLt2M = file.size / 1024 / 1024 < 2;
- const isJPG = file.type === "image/jpeg" || file.type === "image/png";
- if (!isJPG) {
- ElMessage.warning("请上传jpg、png格式,大小不超过2M的照片");
- return false;
+// 设备类型触发事件
+const handleDeviceTypeChange = async (deviceTypeId: string | number) => {
+ // 重置规则和显示状态
+ rules.value.deviceMac = [];
+ rules.value.deviceImei = [];
+ showMacField.value = false;
+ showImeiField.value = false;
+ communicationModeInfo.value = null;
+
+ // 编辑时如果有值,根据已有值确定显示哪个字段
+ if (form.value.id) {
+ if (form.value.deviceMac) {
+ showMacField.value = true;
+ rules.value.deviceMac = [{ required: true, message: '请输入设备MAC', trigger: 'blur' }];
+ } else if (form.value.deviceImei) {
+ showImeiField.value = true;
+ rules.value.deviceImei = [{ required: true, message: '请输入设备IMEI', trigger: 'blur' }];
+ }
+ return;
+ }
+ // 新增或编辑时没有值,根据设备类型获取通讯方式
+ try {
+ userFormRef.value?.clearValidate(['deviceMac', 'deviceImei']);
+ if (!deviceTypeId) {
+ return;
+ }
+ const res = await api.getCommunicationMode({ id: deviceTypeId });
+ if (res.code === 0 && res.data) {
+ communicationModeInfo.value = res.data;
+ // 根据通讯方式确定显示哪个字段
+ if (res.data.communicationMode === '1') { // 蓝牙设备 - 显示MAC
+ showMacField.value = true;
+ showImeiField.value = false;
+ rules.value.deviceMac = [{ required: true, message: '请输入设备MAC', trigger: 'blur' }];
+ form.value.deviceImei = ''; // 清空IMEI
+ } else if (res.data.communicationMode === '0') { // 4G设备 - 显示IMEI
+ showMacField.value = false;
+ showImeiField.value = true;
+ rules.value.deviceImei = [{ required: true, message: '请输入设备IMEI', trigger: 'blur' }];
+ form.value.deviceMac = ''; // 清空MAC
}
- if (!isLt2M) {
- ElMessage.warning("大小不超过2M的照片片");
- return false;
- }
- return isJPG && isLt2M;
- };
- // 文件上传状态改变时触发
- const fileUploadChange=(files, fileList)=> {
- console.log(fileList, '5555');
- if (fileList.length > 0) {
- form.value.image = fileList[0].raw; // 正确获取File对象
- // 调试:检查是否是真正的 File 对象
- console.log('File对象验证:', form.value.image);
- } else {
- form.value.image = null;
- }
- };
+ } else {
+ proxy?.$modal.msgError(res.msg || '获取通讯方式失败');
+ }
+ } catch (error) {
+ proxy?.$modal.msgError('获取通讯方式失败');
+ }
+};
+
+// 覆盖默认的上传行为,可以自定义上传的实现
+const httpRequestImg = (parm) => { };
+const beforeUpload = (file) => {
+ const isLt2M = file.size / 1024 / 1024 < 2;
+ const isJPG = file.type === "image/jpeg" || file.type === "image/png";
+ if (!isJPG) {
+ ElMessage.warning("请上传jpg、png格式,大小不超过2M的照片");
+ return false;
+ }
+ if (!isLt2M) {
+ ElMessage.warning("大小不超过2M的照片片");
+ return false;
+ }
+ return isJPG && isLt2M;
+};
+// 文件上传状态改变时触发
+const fileUploadChange = (files, fileList) => {
+ console.log(fileList, '5555');
+ if (fileList.length > 0) {
+ form.value.image = fileList[0].raw; // 正确获取File对象
+ // 调试:检查是否是真正的 File 对象
+ console.log('File对象验证:', form.value.image);
+ } else {
+ form.value.image = null;
+ }
+};
/** 提交按钮 */
-const submitForm = () => {
- userFormRef.value?.validate(async (valid: boolean) => {
- if (valid) {
- // try {
- // const formData = new FormData();
- // // 处理图片字段
- // if (this.form.image instanceof File) {
- // formData.append('file', this.form.image);
- // } else if (this.form.image && typeof this.form.image === 'string') {
- // // 编辑时未修改图片,传递原图片URL
- // formData.append('imageUrl', this.form.image);
- // }
- // // 添加其他必要字段
- // const fields = ['id', 'deviceName', 'deviceType', 'remark'];
- // fields.forEach(key => {
- // if (this.form[key] !== undefined && this.form[key] !== null) {
- // formData.append(key, this.form[key]);
- // }
- // });
- // // 根据通讯方式,有条件地添加deviceMac或deviceImei
- // if (this.form.deviceMac) {
- // formData.append('deviceMac', this.form.deviceMac)
- // }
- // if (this.form.deviceImei) {
- // formData.append('deviceImei', this.form.deviceImei)
- // }
- // // 根据操作类型设置URL和方法
- // const isAdd = this.crud.status.add;
- // const res = await request({ // 使用 await 等待结果
- // url: isAdd ? '/api/device/add' : '/api/device/update',
- // method: isAdd ? 'post' : 'put',
- // data: formData,
- // headers: {
- // 'Content-Type': 'multipart/form-data'
- // }
- // });
- // if (res.code == 0) {
- // this.$message.success(isAdd ? '添加成功' : '编辑成功');
- // this.crud.cancelCU();
- // this.crud.toQuery();
- // } else {
- // this.$message.warning(res.msg);
- // }
- // } catch (err) {
- // this.$message.warning('操作失败');
- // console.error(err);
- // }
+const submitForm = async () => {
+ try {
+ const valid = await userFormRef.value?.validate();
+ if (!valid) return;
+ loadingIng.value = true;
+ const formData = new FormData();
+ // 处理图片字段
+ if (form.value.image instanceof File) {
+ formData.append('file', form.value.image);
+ } else if (form.value.image && typeof form.value.image === 'string') {
+ // 如果是URL且需要转换为二进制
+ const blob = await urlToBlob(form.value.image);
+ formData.append('file', blob, 'image.jpg'); // 添加文件名
}
- });
+ // 添加其他必要字段
+ const fields = ['id', 'deviceName', 'deviceType', 'remark'];
+ fields.forEach(key => {
+ if (form.value[key] !== undefined && form.value[key] !== null) {
+ formData.append(key, String(form.value[key])); // 确保所有值都转为字符串
+ }
+ });
+ // 根据通讯方式,有条件地添加deviceMac或deviceImei
+ if (form.value.deviceMac) {
+ formData.append('deviceMac', form.value.deviceMac);
+ }
+ if (form.value.deviceImei) {
+ formData.append('deviceImei', form.value.deviceImei);
+ }
+ // 根据操作类型设置URL和方法
+ const isAdd = !form.value.id; // 注意这里逻辑反了,应该是没有id时是新增
+ const res = await request({
+ url: isAdd ? '/api/device/add' : '/api/device/update',
+ method: isAdd ? 'post' : 'put',
+ data: formData,
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+ if (res.code == 0) {
+
+ proxy?.$modal.msgSuccess('操作成功');
+ dialog.visible = false;
+ loadingIng.value = false;
+ await getList();
+ } else {
+ proxy?.$modal.msgWarning(res.msg)
+ loadingIng.value = false;
+ }
+ } catch (err) {
+ console.error(err);
+ loadingIng.value = false;
+ }
+};
+
+// URL转Blob的方法函数
+const urlToBlob = async (url: string): Promise => {
+ const response = await fetch(url);
+ if (!response.ok) throw new Error('图片加载失败');
+ return await response.blob();
};
/**
@@ -432,9 +653,161 @@ const getDeviceType = () => {
})
};
+// 客户下拉框
+const getAllCustomerAll = () => {
+ api.userAllCustomerAll().then(res => {
+ if (res.code == 0) {
+ customerList.value = res.data
+ }
+ })
+};
+// 分配客户
+const assignRow = ref()
+const handleAssign = (row: any) => {
+ getAllCustomerAll()
+ assignDialogVisible.value = true
+ assignRow.value = row
+ assignCustomerId.value = row.customerId
+}
+const handleAssignConfirm = () => {
+ if (!assignCustomerId.value) {
+ return proxy?.$modal.msgError('请选择客户')
+ }
+ loadingIng.value = true;
+ // 这里调用分配API
+ let data = {
+ customerId: assignCustomerId.value,
+ deviceIds: [assignRow.value.id]
+ }
+ api.deviceAssignCustomer(data).then((res) => {
+ if (res.code == 0) {
+ loadingIng.value = false;
+ const customer = customerList.value.find(c => c.id === assignCustomerId.value)
+ const customerName = customer ? customer.nickName : `ID: ${assignCustomerId.value}`
+ getList();
+ assignDialogVisible.value = false
+ return proxy?.$modal.msgSuccess(`设备已分配给客户: ${customerName}`)
+
+ } else {
+ loadingIng.value = false;
+ }
+ })
+};
+const importUpload = ref()
+const importResult = ref()
+const handleBatchImport = () => {
+ importDialogVisible.value = true
+ importResult.value = {
+ isShow: false,
+ total: 0,
+ succeed: 0,
+ errorSun: 0,
+ link: ''
+ }
+ nextTick(() => {
+ if (importUpload.value) {
+ importUpload.value.clearFiles()
+ }
+ })
+};
+const downloadTemplate = () => {
+ // 这里可用 window.open 或 a 标签下载模板
+ const link = document.createElement('a');
+ link.href = 'http://fuyuanshen.com:81/images/excel/equipmenttemplate.xlsx';
+ link.download = '设备数据导入模板.xlsx'; // 可选:指定下载文件名
+ link.style.display = 'none'; // 隐藏标签
+ document.body.appendChild(link);
+ link.click(); // 触发下载
+ document.body.removeChild(link); // 移除标签
+};
+const beforeImportUpload = (file: any) => {
+ const isLt5M = file.size / 1024 / 1024 < 5
+ if (!isLt5M) {
+ proxy?.$modal.msgError('上传文件大小不能超过 5MB!')
+ }
+ return isLt5M
+};
+
+//添加tokenf方法head_upload 直接返回 getBearerToken()
+const head_upload = () => getBearerToken();
+const handleImportSuccess = (response: any) => {
+ if (response.code === 0) {
+ importResult.value.isShow = true
+ if (response.data) {
+ importResult.value.succeed = response.data.successCount || 0
+ importResult.value.errorSun = response.data.failureCount || 0
+ importResult.value.total = importResult.value.succeed + importResult.value.errorSun
+ importResult.value.link = response.data.errorExcelUrl || ''
+ }
+ getList(); // 初始化列表数据
+ } else {
+ proxy?.$modal.msgError(response.msg || '导入失败')
+ }
+};
+const handleImportError = () => {
+ proxy?.$modal.msgError('导入失败')
+};
+// 批量分配客户
+const handleBatchAssign = () => {
+ batchAssignDialogVisible.value = true
+ getAllCustomerAll()
+};
+// 批量分配客户确定
+const handleBatchAssignConfirm = () => {
+ if (!batchAssignCustomerId.value) {
+ return proxy?.$modal.msgError('请选择客户')
+ }
+ // 这里可以调用批量分配API,传递 crud.selections 和 batchAssignCustomerId
+ // 提取选中设备的 ID 数组
+ const selectedIds = ids.value.map((item) => item.id)
+ // 构造请求数据
+ const data = {
+ customerId: batchAssignCustomerId.value, // 目标客户ID
+ deviceIds: selectedIds // 选中的设备ID数组
+ }
+ api.deviceAssignCustomer(data).then((res) => {
+ if (res.code == 0) {
+ batchAssignDialogVisible.value = false
+ getList();
+ return proxy?.$modal.msgSuccess(`分配成功`)
+ }
+ })
+};
+
+
+
+watch(() => form.value.deviceType, (newVal) => {
+ if (dialog.title === '新增设备') { // Only for add form
+ handleDeviceTypeChange(newVal);
+ }
+});
onMounted(() => {
getList(); // 初始化列表数据
getDeviceType()
});
+
+
diff --git a/src/views/index.vue b/src/views/index.vue
index e5c35a6..03c08f4 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -2,92 +2,8 @@
- RuoYi-Vue-Plus多租户管理系统
-
- RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
-
- * 前端开发框架 Vue3、TS、Element Plus
- * 后端开发框架 Spring Boot
- * 容器框架 Undertow 基于 Netty 的高性能容器
- * 权限认证框架 Sa-Token 支持多终端认证系统
- * 关系数据库 MySQL 适配 8.X 最低 5.7
- * 缓存数据库 Redis 适配 6.X 最低 4.X
- * 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率
- * 数据库框架 p6spy 更强劲的 SQL 分析
- * 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构
- * 序列化框架 Jackson 统一使用 jackson 高效可靠
- * Redis客户端 Redisson 性能强劲、API丰富
- * 分布式限流 Redisson 全局、请求IP、集群ID 多种限流
- * 分布式锁 Lock4j 注解锁、工具锁 多种多样
- * 分布式幂等 Lock4j 基于分布式锁实现
- * 分布式链路追踪 SkyWalking 支持链路追踪、网格分析、度量聚合、可视化
- * 分布式任务调度 SnailJob 高性能 高可靠 易扩展
- * 文件存储 Minio 本地存储
- * 文件存储 七牛、阿里、腾讯 云存储
- * 监控框架 SpringBoot-Admin 全方位服务监控
- * 校验框架 Validation 增强接口安全性 严谨性
- * Excel框架 FastExcel(原Alibaba EasyExcel) 性能优异 扩展性强
- * 文档框架 SpringDoc、javadoc 无注解零入侵基于java注释
- * 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性
- * 代码生成器 适配MP、SpringDoc规范化代码 一键生成前后端代码
- * 部署方式 Docker 容器编排 一键部署业务集群
- * 国际化 SpringMessage Spring标准国际化方案
-
- 当前版本: v5.4.0
-
- ¥免费开源
-
-
- 访问码云
- 访问GitHub
- 更新日志
-
-
- RuoYi-Cloud-Plus多租户微服务管理系统
-
- RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
-
- * 前端开发框架 Vue3、TS、Element UI
- * 后端开发框架 Spring Boot
- * 微服务开发框架 Spring Cloud、Spring Cloud Alibaba
- * 容器框架 Undertow 基于 XNIO 的高性能容器
- * 权限认证框架 Sa-Token、Jwt 支持多终端认证系统
- * 关系数据库 MySQL 适配 8.X 最低 5.7
- * 关系数据库 Oracle 适配 11g 12c
- * 关系数据库 PostgreSQL 适配 13 14
- * 关系数据库 SQLServer 适配 2017 2019
- * 缓存数据库 Redis 适配 6.X 最低 5.X
- * 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能
- * 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能
- * 服务网关 Spring Cloud Gateway 响应式高性能网关
- * 负载均衡 Spring Cloud Loadbalancer 负载均衡处理
- * RPC远程调用 Apache Dubbo 原生态使用体验、高性能
- * 分布式限流熔断 Alibaba Sentinel 无侵入、高扩展
- * 分布式事务 Alibaba Seata 无侵入、高扩展 支持 四种模式
- * 分布式消息队列 Apache Kafka 高性能高速度
- * 分布式消息队列 Apache RocketMQ 高可用功能多样
- * 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性
- * 分布式搜索引擎 ElasticSearch 业界知名
- * 分布式链路追踪 Apache SkyWalking 链路追踪、网格分析、度量聚合、可视化
- * 分布式日志中心 ELK 业界成熟解决方案
- * 分布式监控 Prometheus、Grafana 全方位性能监控
- * 其余与 Vue 版本一致
-
- 当前版本: v2.4.0
-
- ¥免费开源
-
-
- 访问码云
- 访问GitHub
- 更新日志
-
@@ -102,64 +18,6 @@ const goTarget = (url: string) => {
diff --git a/src/views/login.vue b/src/views/login.vue
index f641549..1f554d5 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -6,44 +6,35 @@
-
-
+
+
-
+
-
+
-
+
- {{ proxy.$t('login.rememberPassword') }}
+
{{ proxy.$t('login.login') }}
@@ -71,10 +62,6 @@
-
-
@@ -261,8 +248,10 @@ onMounted(() => {
width: 400px;
padding: 25px 25px 5px 25px;
z-index: 1;
+
.el-input {
height: 40px;
+
input {
height: 40px;
}
diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue
index c6d83b4..012a1c7 100644
--- a/src/views/system/role/index.vue
+++ b/src/views/system/role/index.vue
@@ -49,7 +49,7 @@
删除
- 导出
+ 导出