Compare commits

..

2 Commits

Author SHA1 Message Date
sjl
1d849bcff9 Merge branch 'main' of http://192.168.1.22:3000/Web/admin-govern 2025-11-14 16:19:47 +08:00
sjl
e9b09e2193 正式用户权限分配 2025-11-14 16:19:18 +08:00
8 changed files with 425 additions and 3 deletions

View File

@@ -53,3 +53,25 @@ export const removeMarketData = (data: any) => {
params: data params: data
}) })
} }
/**
* 获取用户未绑定的在运的便携式设备
*/
export const queryRunPortableDevByUseId = (data: any) => {
return createAxios({
url: '/cs-device-boot/EquipmentDelivery/getRunPortableDev',
method: 'post',
params: data
})
}
/**
* 获取用户未绑定的工程信息
*/
export const queryUnlinkEngineeringByUseId = (data: any) => {
return createAxios({
url: '/cs-device-boot/engineering/getUnlinkedEngineering',
method: 'post',
params: data
})
}

View File

@@ -0,0 +1,29 @@
import createAxios from '@/utils/request'
// * 根据用户id查询工程和便携式设备
export const queryDevByUseId = (data: any) => {
return createAxios({
url: '/cs-system-boot/wlUser/selectDevByUserId',
method: 'POST',
params: data
})
}
// * 用户绑定工程和便携式设备
export const add = (data: any) => {
return createAxios({
url: '/cs-system-boot/wlUser/addUserDev',
method: 'POST',
data: data
})
}
/**
* 用户取消绑定工程和便携式设备
*/
export const removeUserDev = (data: any) => {
return createAxios({
url: '/cs-system-boot/wlUser/deleteUserDev',
method: 'post',
data: data
})
}

View File

@@ -0,0 +1,11 @@
import request from '@/utils/request'
/**
* 获取移动端、便携式正式用户列表
* @returns {AxiosPromise}
*/
export const getFormalUserList = () => {
return request({
url: '/user-boot/user/getFormalUserList',
method: 'post'
})
}

View File

@@ -14,7 +14,7 @@
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse' :class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse'
style='cursor: pointer' v-if='props.canExpand' /> --> style='cursor: pointer' v-if='props.canExpand' /> -->
</div> </div>
<el-tree :style="{ height: 'calc(100vh - 200px)' }" <el-tree :style="{ height: 'calc(100vh - 110px)' }"
style=' overflow: auto;' ref='treeRef' :props='defaultProps' highlight-current :default-expand-all="false" style=' overflow: auto;' ref='treeRef' :props='defaultProps' highlight-current :default-expand-all="false"
@check-change="checkTreeNodeChange" :filter-node-method='filterNode' node-key='id' v-bind='$attrs'> @check-change="checkTreeNodeChange" :filter-node-method='filterNode' node-key='id' v-bind='$attrs'>
<template #default='{ node, data }'> <template #default='{ node, data }'>

View File

@@ -3,7 +3,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getMarketList } from '@/api/user-boot/user' import { getMarketList } from '@/api/user-boot/user'
import Tree from '../index.vue' import Tree from '../cloudDevice.vue'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { ref, reactive, nextTick } from 'vue' import { ref, reactive, nextTick } from 'vue'
const config = useConfig() const config = useConfig()

View File

@@ -0,0 +1,29 @@
<template>
<Tree ref="treRef" :data="tree" />
</template>
<script setup lang="ts">
import { getFormalUserList } from '@/api/user-boot/official'
import Tree from '../cloudDevice.vue'
import { useConfig } from '@/stores/config'
import { ref, reactive, nextTick } from 'vue'
const config = useConfig()
const tree = ref()
const treRef = ref()
const emit = defineEmits(['selectUser'])
getFormalUserList().then((res: any) => {
if (res.code === 'A0000') {
tree.value = res.data.map((item: any) => {
return {
...item,
icon: 'el-icon-User',
color: 'royalblue'
}
})
emit('selectUser', tree.value[0])
nextTick(() => {
treRef.value.treeRef.setCurrentKey(tree.value[0].id)
})
}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -68,7 +68,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
const pageHeight = mainHeight(20) const pageHeight = mainHeight(20)
const loading = ref(true) const loading = ref(true)
const tableHeight = mainHeight(140) const tableHeight = mainHeight(135)
const user: any = ref({}) const user: any = ref({})
const tableData = ref([]) const tableData = ref([])
const tableData2 = ref([]) const tableData2 = ref([])

View File

@@ -0,0 +1,331 @@
<template>
<div class="default-main device-manage" :style="{ height: pageHeight.height }" v-loading="loading">
<OfficialUserTree @node-click="selectUser" @selectUser="selectUser"></OfficialUserTree>
<div class="device-manage-right" :style="{ height: pageHeight.height }">
<div class="el-descriptions__header">
<el-button type="primary" @click="getEnginnerDev">添加工程 / 设备</el-button>
</div>
<!-- 使用flex布局平均分配高度 -->
<div class="tables-container">
<div class="table-wrapper">
<vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%">
<vxe-column field="name" title="工程名称"></vxe-column>
<vxe-column title="操作" width="200px">
<template v-slot:default="scoped">
<el-button link size="small" type="danger" @click="deleteEngineering(scoped.row)">
移除
</el-button>
</template>
</vxe-column>
</vxe-table>
</div>
<!-- 新增设备列表 -->
<div class="device-list-section table-wrapper">
<vxe-table v-bind="defaultAttribute" :data="deviceTableData" height="auto" style="width: 100%">
<vxe-column field="name" title="设备名称"></vxe-column>
<vxe-column title="操作" width="200px">
<template v-slot:default="scoped">
<el-button link size="small" type="danger" @click="deleteDevice(scoped.row)">
移除
</el-button>
</template>
</vxe-column>
</vxe-table>
</div>
</div>
</div>
<!-- 对话框为左右布局 -->
<el-dialog
v-model.trim="dialogVisible"
title="添加工程 / 设备"
class="cn-operate-dialog"
:close-on-click-modal="false"
>
<div class="dialog-content">
<!-- 工程部分 -->
<div class="dialog-section">
<div class="section-header">
<span>工程列表</span>
<el-input
maxlength="32"
show-word-limit
v-model.trim="filterText"
placeholder="搜索工程"
clearable
class="search-input"
>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
</div>
<vxe-table
ref="tableRef"
v-bind="defaultAttribute"
:data="tableData2.filter((item: any) => {
return item.name.includes(filterText)
})"
height="400px"
style="width: 100%"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="name" title="工程名称"></vxe-column>
</vxe-table>
</div>
<!-- 设备部分 -->
<div class="dialog-section">
<div class="section-header">
<span>设备列表</span>
<el-input
maxlength="32"
show-word-limit
v-model.trim="deviceFilterText"
placeholder="搜索设备"
clearable
class="search-input"
>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
</div>
<vxe-table
ref="deviceTableRef"
v-bind="defaultAttribute"
:data="deviceTableData2.filter((item: any) => {
return item.name.includes(deviceFilterText)
})"
height="400px"
style="width: 100%"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="name" title="设备名称"></vxe-column>
</vxe-table>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="addData"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: 'govern/officialUser/index'
})
import { defaultAttribute } from '@/components/table/defaultAttribute'
import OfficialUserTree from '@/components/tree/govern/officialUserTree.vue'
import { mainHeight } from '@/utils/layout'
import { queryRunPortableDevByUseId ,queryUnlinkEngineeringByUseId} from '@/api/cs-device-boot/user'
import { add, removeUserDev, queryDevByUseId } from '@/api/cs-system-boot/official'
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
const pageHeight = mainHeight(20)
const loading = ref(true)
const user: any = ref({})
const tableData = ref([])
const deviceTableData = ref([]) // 设备表格数据
const tableData2 = ref([]) // 工程数据源
const deviceTableData2 = ref([]) // 设备数据源
const dialogVisible = ref(false)
const filterText = ref('') // 工程搜索文本
const deviceFilterText = ref('') // 设备搜索文本
const tableRef = ref()
const deviceTableRef = ref() // 设备表格引用
const selectUser = (e: any) => {
user.value = e
loading.value = true
queryDevByUseId({ userId: e.id }).then((engineeringRes) => {
loading.value = false
tableData.value = engineeringRes.data.engineeringList || []
deviceTableData.value = engineeringRes.data.portableDevList || []
}).catch(() => {
loading.value = false
})
}
const getEnginnerDev = () => {
filterText.value = ''
deviceFilterText.value = ''
// 同时获取工程和设备数据
Promise.all([
queryUnlinkEngineeringByUseId({ userId: user.value.id }),
queryRunPortableDevByUseId({ userId: user.value.id })
]).then(([engineeringRes, deviceRes]) => {
tableData2.value = engineeringRes.data || []
deviceTableData2.value = deviceRes.data || []
dialogVisible.value = true
setTimeout(() => {
tableRef.value?.clearCheckboxRow()
deviceTableRef.value?.clearCheckboxRow()
}, 0)
})
}
const deleteEngineering = (row: any) => {
ElMessageBox.confirm('是否移出该工程?', '请确认', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 只移除工程,设备列表为空
const form = {
engineeringList: [row.id], // 要移除的工程ID
portableDevList: [], // 设备列表为空
userId: user.value.id
}
removeUserDev(form).then((res: any) => {
ElMessage.success(res.message)
selectUser(user.value)
})
})
}
// 删除设备方法
const deleteDevice = (row: any) => {
console.log('删除设备', row)
ElMessageBox.confirm('是否移出该设备?', '请确认', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 只移除设备,工程列表为空
const form = {
engineeringList: [], // 工程列表为空
portableDevList: [row.id], // 要移除的设备ID
userId: user.value.id
}
removeUserDev(form).then((res: any) => {
ElMessage.success(res.message)
selectUser(user.value)
})
})
}
const addData = () => {
const selectedEngineers = tableRef.value.getCheckboxRecords()
const selectedDevices = deviceTableRef.value.getCheckboxRecords()
// 如果没有选择任何项,则提示
if (selectedEngineers.length === 0 && selectedDevices.length === 0) {
ElMessage.warning('请至少选择一项工程或设备')
return
}
// 构造请求参数对象
const form = {
engineeringList: [] as any[],
portableDevList: [] as any[],
userId: user.value.id
}
// // 处理已有的工程数据
// tableData.value.forEach((item: any) => {
// form.engineeringList.push(item.id)
// })
// // 处理已有的设备数据
// deviceTableData.value.forEach((item: any) => {
// form.portableDevList.push(item.id)
// })
// 添加新选择的工程
selectedEngineers.forEach((item: any) => {
form.engineeringList.push( item.id)
})
// 添加新选择的设备
selectedDevices.forEach((item: any) => {
form.portableDevList.push(item.id)
})
// 发送请求
add(form).then((res: any) => {
ElMessage.success(res.message)
selectUser(user.value)
dialogVisible.value = false
})
}
</script>
<style lang="scss" scoped>
.device-manage {
display: flex;
&-right {
overflow: hidden;
flex: 1;
padding: 10px 10px 10px 0;
display: flex;
flex-direction: column;
.el-descriptions__header {
height: 27px;
display: flex;
justify-content: flex-end; /* 靠右显示 */
align-items: center;
}
.tables-container {
flex: 1;
display: flex;
flex-direction: column;
.table-wrapper {
flex: 1;
min-height: 0;
&:first-child {
margin-bottom: 10px;
}
}
}
}
}
.dialog-content {
display: flex;
gap: 20px;
.dialog-section {
flex: 1;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
span {
font-weight: bold;
font-size: 16px;
}
.search-input {
width: 200px;
}
}
}
}
</style>