1
0
forked from dyf/dyf-vue-ui

Compare commits

9 Commits
main ... main

Author SHA1 Message Date
1db495b08e 更新设备导入模版地址 2025-07-19 08:43:58 +08:00
e1738a6ea1 客户管理,账号状态修改 2025-07-18 14:54:44 +08:00
84aa490c20 修复设备列表bug 2025-07-18 09:37:57 +08:00
fb8bbbf5f5 修复编辑设备bug 2025-07-18 09:27:15 +08:00
a195b3e325 修复设备列表bug 2025-07-17 16:56:21 +08:00
c1adbbb9fe Merge branch 'main' of http://47.107.152.87:3000/dyf/dyf-vue-ui 2025-07-14 17:13:47 +08:00
ae445e058f 提交 2025-07-14 17:12:55 +08:00
5835c7c3fe 更新配置环境 2025-07-14 17:12:29 +08:00
a6b07348a6 查看用户绑定,解绑接口对接联调 2025-07-14 15:34:38 +08:00
8 changed files with 106 additions and 79 deletions

View File

@ -1,11 +1,17 @@
# 页面标题 # 页面标题
VITE_APP_TITLE = 物联网管理系统 VITE_APP_TITLE = 物联网管理系统
# 生产环境配置 # 生产环境配置 晶全1
VITE_APP_ENV = 'https://fuyuanshen.com/backend' VITE_APP_ENV = 'https://fuyuanshen.com/backend'
# 应用访问路径 例如使用前缀 /admin/ # 生产环境配置 富源晟2
VITE_APP_CONTEXT_PATH = '/sys/' # VITE_APP_ENV = 'https://fuyuanshen.com/backend-fys'
# 应用访问路径 晶全1
VITE_APP_CONTEXT_PATH = '/jingquan/'
# 应用访问路径 富源晟2
#VITE_APP_CONTEXT_PATH = '/sys/'
# 监控地址 # 监控地址
VITE_APP_MONITOR_ADMIN = '/admin/applications' VITE_APP_MONITOR_ADMIN = '/admin/applications'
@ -13,9 +19,12 @@ VITE_APP_MONITOR_ADMIN = '/admin/applications'
# SnailJob 控制台地址 # SnailJob 控制台地址
VITE_APP_SNAILJOB_ADMIN = '/snail-job' VITE_APP_SNAILJOB_ADMIN = '/snail-job'
# 生产环境 # 生产环境 晶全3
VITE_APP_BASE_API = '/backend' VITE_APP_BASE_API = '/backend'
# 生产环境 富源晟3
#VITE_APP_BASE_API = '/backend-fys'
# 是否在打包时开启压缩,支持 gzip 和 brotli # 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip VITE_BUILD_COMPRESS = gzip

View File

@ -72,12 +72,11 @@ export const userAllCustomerAll = () => {
} }
// 解绑 // 解绑
export const deviceUnbind = (params:any) => {
export const deviceUnbind = (data:any) => {
return request({ return request({
url: '/api/device/unbind', url: '/api/device/unbind',
method: 'post', method: 'GET',
data params
}) })
} }
// 撤回 // 撤回

View File

@ -4,7 +4,7 @@ import { AxiosPromise } from 'axios';
* 查询设备列表 * 查询设备列表
* @param query * @param query
*/ */
export const userList = (params): AxiosPromise => { export const userList = (params: any): AxiosPromise => {
return request({ return request({
url: '/app/user/list', url: '/app/user/list',
method: 'get', method: 'get',
@ -12,7 +12,7 @@ export const userList = (params): AxiosPromise => {
}); });
}; };
// 绑定设备列表 // 绑定设备列表
export const deviceList = (params): AxiosPromise => { export const deviceList = (params: any): AxiosPromise => {
return request({ return request({
url: '/api/app/device', url: '/api/app/device',
method: 'get', method: 'get',
@ -20,12 +20,24 @@ export const deviceList = (params): AxiosPromise => {
}); });
}; };
// 账号状态 // 账号状态
export const userStatus = (data): AxiosPromise => { export const userStatus = (data: any): AxiosPromise => {
return request({ return request({
url: '/api/app/device', url: '/api/app/device',
method: 'put', method: 'put',
data data
}); });
}; };
// 解绑设备
export const deviceUnBind = (data: any): AxiosPromise => {
return request({
url: '/api/app/device/unBind',
method: 'delete',
data,
headers: {
'Content-Type': 'multipart/form-data', //设置正确的 Content-Type
},
});
};
export default { userList,deviceList,userStatus }
export default { userList, deviceList, userStatus, deviceUnBind }

View File

@ -1,29 +1,6 @@
import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router'; import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router';
/* Layout */ /* Layout */
import Layout from '@/layout/index.vue'; import Layout from '@/layout/index.vue';
/**
* Note: 路由配置项
*
* hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401login等页面或者如一些编辑页面/edit/1
* alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时自动会变成嵌套的模式--如组件页面
* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
* // 若你想不管路由下面的 children 声明的个数都显示你的根路由
* // 你可以设置 alwaysShow: true这样它就会忽略之前定义的规则一直显示根路由
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
* roles: ['admin', 'common'] // 访问路由的角色权限
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
* meta : {
noCache: true // 如果设置为true则不会被 <keep-alive> 缓存(默认 false)
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' // 设置该路由的图标对应路径src/assets/icons/svg
breadcrumb: false // 如果设置为false则不会在breadcrumb面包屑中显示
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
}
*/
// 公共路由 // 公共路由
export const constantRoutes: RouteRecordRaw[] = [ export const constantRoutes: RouteRecordRaw[] = [
{ {

View File

@ -34,9 +34,9 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间" align="center" prop="loginDate" width="180"> <el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ proxy.parseTime(scope.row.loginDate) }}</span> <span>{{ proxy.parseTime(scope.row.createTime) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" fixed="right" align="center"> <el-table-column label="操作" fixed="right" align="center">

View File

@ -16,18 +16,18 @@
<el-table-column prop="deviceName" label="设备名称" /> <el-table-column prop="deviceName" label="设备名称" />
<el-table-column prop="devicePic" label="设备图片"> <el-table-column prop="devicePic" label="设备图片">
<template #default="scope"> <template #default="scope">
<el-image style="width: 40px; height: 40px" :src="scope.row.devicePic" <el-popover placement="right" trigger="click">
:preview-src-list="[scope.row.devicePic]" /> <template #reference>
<img :src="scope.row.devicePic" style="width: 40px; height: 40px; cursor: pointer;"
class="hover:opacity-80 transition-opacity" />
</template>
<img :src="scope.row.devicePic" style="max-width: 600px; max-height: 600px;" />
</el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="deviceMac" label="设备MAC" /> <el-table-column prop="deviceMac" label="设备MAC" />
<el-table-column prop="deviceImei" label="设备IMEI" /> <el-table-column prop="deviceImei" label="设备IMEI" />
<el-table-column prop="deviceTypeName" label="设备类型" /> <el-table-column prop="typeName" label="设备类型" />
<el-table-column prop="deviceStatus" label="状态">
<template #default="scope">
<span>{{ scope.row.deviceStatus === 1 ? '正常' : '失效' }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="绑定日期" /> <el-table-column prop="createTime" label="绑定日期" />
<el-table-column label="操作" width="100" align="center"> <el-table-column label="操作" width="100" align="center">
<template #default="scope"> <template #default="scope">
@ -58,8 +58,6 @@ const queryParams = reactive({
deviceImei: '' deviceImei: ''
}) })
function openDialog(row: OperLogForm) { function openDialog(row: OperLogForm) {
console.log(row,'roe11');
info.value = row; info.value = row;
open.value = true; open.value = true;
getList(); getList();
@ -76,27 +74,40 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryParams.pageNum = 1; queryParams.pageNum = 1;
queryParams.deviceImei = '',
queryParams.deviceMac = '',
queryParams.deviceName = '',
getList()
}; };
// 列表 // 列表
const getList = async () => { const getList = async () => {
bindingLoading.value = true; bindingLoading.value = true;
const res = await api.deviceList(queryParams); let data = {
bindingUserId: info.value.userId,
pageNum: queryParams.pageNum,
pageSize: queryParams.pageSize,
deviceName: queryParams.deviceName,
deviceMac: queryParams.deviceMac,
deviceImei: queryParams.deviceImei
}
const res = await api.deviceList(data);
boundDevices.value = res.rows; boundDevices.value = res.rows;
total.value = res.total; total.value = res.total;
bindingLoading.value = false; bindingLoading.value = false;
}; };
// 解绑 // 解绑
const handleUnbind = (row) => { const handleUnbind = (row) => {
proxy?.$modal.confirm.confirm('此操作将解绑设备 "' + row.deviceName + '", 是否继续?', '提示', { proxy?.$modal.confirm('此操作将解绑设备 "' + row.deviceName + '", 是否继续?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
const data = { const data = {
deviceMac: row.deviceMac, // deviceMac: row.deviceMac,
customerId: info.value.id id: row.id
} }
api.deviceList(data).then(() => { api.deviceUnBind(data).then(() => {
proxy?.$modal.msgSuccess({ type: 'success', message: '解绑成功!' }) proxy?.$modal.msgSuccess({ type: 'success', message: '解绑成功!' })
getList() getList()
}) })

View File

@ -9,10 +9,10 @@
<el-input v-model="queryParams.blurry" placeholder="请输入客户名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.blurry" placeholder="请输入客户名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="客户状态" prop="status"> <el-form-item label="客户状态" prop="status">
<el-select v-model="queryParams.enabled" clearable placeholder="客户状态" class="filter-item"> <el-select v-model="queryParams.status" clearable placeholder="客户状态" class="filter-item">
<el-option label="全部" :value="''" /> <el-option label="全部" :value="''" />
<el-option label="启用" :value="true" /> <el-option label="启用" :value="0" />
<el-option label="禁用" :value="false" /> <el-option label="禁用" :value="1" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="创建时间" style="width: 308px"> <el-form-item label="创建时间" style="width: 308px">
@ -53,10 +53,10 @@
<el-table-column type="selection" width="50" align="center" /> <el-table-column type="selection" width="50" align="center" />
<el-table-column label="客户名称" align="center" prop="nickName" /> <el-table-column label="客户名称" align="center" prop="nickName" />
<el-table-column label="客户账号" align="center" prop="userName" :show-overflow-tooltip="true" /> <el-table-column label="客户账号" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="客户状态" align="center" prop="enabled"> <el-table-column label="客户状态" align="center" prop="status">
<template #default="scope"> <template #default="scope">
<div @click="handleStatusChange(scope.row)"> <div @click="handleStatusChange(scope.row)">
<el-switch v-model="scope.row.enabled" /> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -104,7 +104,7 @@
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item v-if="form.customerId === ''" label="账号密码" prop="password"> <el-form-item v-if="form.customerId === ''" label="账号密码" prop="password">
<el-input v-model="form.password" placeholder="请输入账号密码" /> <el-input v-model="form.password" placeholder="请输入账号密码" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -171,8 +171,16 @@ const initData: PageData<UserForm, UserQuery> = {
trigger: 'blur' trigger: 'blur'
} }
], ],
password: [ password: [
{ required: true, message: '请输入账号密码', trigger: 'blur' }, { required: true, message: '请输入账号密码', trigger: 'blur' },
{
min: 5,
max: 20,
message: '用户密码长度必须介于 5 和 20 之间',
trigger: 'blur'
},
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
], ],
} }
}; };
@ -199,6 +207,7 @@ const handleQuery = () => {
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
queryParams.value.status=''
handleQuery(); handleQuery();
}; };
@ -227,19 +236,24 @@ const handleDelete = async (row?: UserVO) => {
/** 用户状态修改 */ /** 用户状态修改 */
const handleStatusChange = async (row: any) => { const handleStatusChange = async (row: any) => {
console.log(row, '33333333'); // 计算新状态(取反)
const newStatus = row.enabled; // 获取新的状态值(取反) const originalStatus = row.status;
const text = newStatus ? '启用' : '停用'; const actionText = row.status == 0 ? '启用' : '停用';
try { try {
await proxy?.$modal.confirm(`确认要${text}"${row.nickName}"客户吗?`); await proxy?.$modal.confirm(`确认要${actionText}"${row.nickName}"客户吗?`);
await api.updateCustomer({ await api.updateCustomer({
customerId: row.customerId, customerId: row.customerId,
enabled: newStatus enabled: originalStatus == 0 ? true : false
}); });
proxy?.$modal.msgSuccess(text + '成功'); proxy?.$modal.msgSuccess(actionText + '成功');
await getList(); await getList();
} catch (err) { } catch (err) {
row.enabled = !newStatus; // 回滚状态 if (err == 'cancel') { // 这里假设confirm方法在用户取消时reject with 'cancel'
console.log(err, 'errr');
proxy?.$modal.msgWarning('操作已取消');
await getList();
}
} }
}; };

View File

@ -405,12 +405,15 @@ const handleUnbind = (row) => {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
api.deviceUnbind(row).then(res => { let data = {
if (res.code === 0) { id: row.id
proxy?.$modal.msgSuccess('解绑成功') }
api.deviceUnbind(data).then(res => {
if (res.code == 200) {
proxy?.$modal.msgSuccess(res.msg)
getList(); // 初始化列表数据 getList(); // 初始化列表数据
} else { } else {
proxy?.$modal.msgError(res.msg || '解绑失败') proxy?.$modal.msgError(res.msg)
} }
}) })
}).catch(() => { }).catch(() => {
@ -425,7 +428,7 @@ const handleWithdraw = (row: any) => {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
api.withdrawDevice([row.id]).then(res => { api.withdrawDevice([row.id]).then(res => {
if (res.code === 0) { if (res.code === 200) {
proxy?.$modal.msgSuccess('撤回成功') proxy?.$modal.msgSuccess('撤回成功')
getList(); // 初始化列表数据 getList(); // 初始化列表数据
} else { } else {
@ -480,6 +483,8 @@ const handleUpdate = async (row?: deviceForm) => {
getDeviceType(); getDeviceType();
try { try {
if (row) { if (row) {
// 使用 nextTick 确保对话框完全渲染后再设置表单值
await nextTick();
Object.assign(form.value, row); Object.assign(form.value, row);
form.value.image = row.devicePic form.value.image = row.devicePic
// 编辑时根据已有值显示字段 // 编辑时根据已有值显示字段
@ -531,7 +536,7 @@ const handleDeviceTypeChange = async (deviceTypeId: string | number) => {
return; return;
} }
const res = await api.getCommunicationMode({ id: deviceTypeId }); const res = await api.getCommunicationMode({ id: deviceTypeId });
if (res.code === 0 && res.data) { if (res.code == 200 && res.data) {
communicationModeInfo.value = res.data; communicationModeInfo.value = res.data;
// 根据通讯方式确定显示哪个字段 // 根据通讯方式确定显示哪个字段
if (res.data.communicationMode == '1') { // 蓝牙设备 - 显示MAC if (res.data.communicationMode == '1') { // 蓝牙设备 - 显示MAC
@ -551,7 +556,7 @@ const handleDeviceTypeChange = async (deviceTypeId: string | number) => {
}; };
// 覆盖默认的上传行为,可以自定义上传的实现 // 覆盖默认的上传行为,可以自定义上传的实现
const httpRequestImg = (parm): Promise<any> => { const httpRequestImg = (parm): Promise<any> => {
return Promise.resolve(); return Promise.resolve();
}; };
const beforeUpload = (file) => { const beforeUpload = (file) => {
@ -622,7 +627,7 @@ const submitForm = async () => {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
} }
}); });
if (res.code == 0) { if (res.code == 200) {
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
loadingIng.value = false; loadingIng.value = false;
@ -663,7 +668,7 @@ const resetForm = () => {
// 设备类型 // 设备类型
const getDeviceType = () => { const getDeviceType = () => {
api.deviceTypeAll().then(res => { api.deviceTypeAll().then(res => {
if (res.code == 0) { if (res.code == 200) {
deviceTypeOptions.value = res.data deviceTypeOptions.value = res.data
} }
}).catch(err => { }).catch(err => {
@ -673,7 +678,7 @@ const getDeviceType = () => {
// 客户下拉框 // 客户下拉框
const getAllCustomerAll = () => { const getAllCustomerAll = () => {
api.userAllCustomerAll().then(res => { api.userAllCustomerAll().then(res => {
if (res.code == 0) { if (res.code == 200) {
customerList.value = res.data customerList.value = res.data
} }
}) })
@ -699,7 +704,7 @@ const handleAssignConfirm = () => {
deviceIds: [assignRow.value.id] deviceIds: [assignRow.value.id]
} }
api.deviceAssignCustomer(data).then((res) => { api.deviceAssignCustomer(data).then((res) => {
if (res.code == 0) { if (res.code == 200) {
loadingIng.value = false; loadingIng.value = false;
const customer = customerList.value.find(c => c.id === assignCustomerId.value) const customer = customerList.value.find(c => c.id === assignCustomerId.value)
const customerName = customer ? customer.nickName : `ID: ${assignCustomerId.value}` const customerName = customer ? customer.nickName : `ID: ${assignCustomerId.value}`
@ -733,7 +738,7 @@ const handleBatchImport = () => {
const downloadTemplate = () => { const downloadTemplate = () => {
// 这里可用 window.open 或 a 标签下载模板 // 这里可用 window.open 或 a 标签下载模板
const link = document.createElement('a'); const link = document.createElement('a');
link.href = 'http://fuyuanshen.com:81/images/excel/equipmenttemplate.xlsx'; link.href = 'https://fuyuanshen.com/fys/Equipmentimporttemplate/EquipmentImportTemplate.xlsx';
link.download = '设备数据导入模板.xlsx'; // 可选:指定下载文件名 link.download = '设备数据导入模板.xlsx'; // 可选:指定下载文件名
link.style.display = 'none'; // 隐藏标签 link.style.display = 'none'; // 隐藏标签
document.body.appendChild(link); document.body.appendChild(link);
@ -751,7 +756,7 @@ const beforeImportUpload = (file: any) => {
//添加tokenf方法head_upload 直接返回 getBearerToken() //添加tokenf方法head_upload 直接返回 getBearerToken()
const head_upload = () => getBearerToken(); const head_upload = () => getBearerToken();
const handleImportSuccess = (response: any) => { const handleImportSuccess = (response: any) => {
if (response.code === 0) { if (response.code == 200) {
importResult.value.isShow = true importResult.value.isShow = true
if (response.data) { if (response.data) {
importResult.value.succeed = response.data.successCount || 0 importResult.value.succeed = response.data.successCount || 0
@ -785,7 +790,7 @@ const handleBatchAssignConfirm = () => {
deviceIds: selectedIds // 选中的设备ID数组 deviceIds: selectedIds // 选中的设备ID数组
} }
api.deviceAssignCustomer(data).then((res) => { api.deviceAssignCustomer(data).then((res) => {
if (res.code == 0) { if (res.code == 200) {
batchAssignDialogVisible.value = false batchAssignDialogVisible.value = false
getList(); getList();
return proxy?.$modal.msgSuccess(`分配成功`) return proxy?.$modal.msgSuccess(`分配成功`)