@zuopngfei 279c2074db ssss
2025-10-19 18:11:11 +08:00

607 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="group-container">
<div class="infinite-list" v-infinite-scroll="listLoad" style="overflow: auto"
:infinite-scroll-immediate="false" infinite-scroll-distance="10">
<el-card class="add-robot">
<div class="add-box" @click="handleAdd">
<el-icon>
<Plus></Plus>
</el-icon>
添加机器人
</div>
</el-card>
<el-card v-for="(item, index) in robotCards" :key="item.index"
:class="{ 'active-robot': newRobotId == item.id }" @click="handleRobot(item)">
<div class="robot-title">
<el-image class="robot-head" :src="item.head_url"></el-image>
<p class="robot-name-box">
<label class="name">{{ item.name }}</label>
<label class="wx_name"> 微信:{{ item.wx_name
}}</label>
<label class="wx_name"> 服务:{{ item.server_ip }}</label>
</p>
</div>
<p class="description">描述:{{ item.description }}</p>
<span v-if="item.status == 1" class="success"> 运行中 </span>
<span v-if="item.status == 2" class="danger"> 异常 </span>
<el-popover @click.stop placement="bottom" width="120px" trigger="hover">
<template #reference>
<span class="edit-icon" @click.stop>
<el-icon>
<MoreFilled />
</el-icon>
</span>
</template>
<template #default>
<ul class="popover-list" @click.stop>
<li class="edit-icon" @click.stop="setMonitor(item)">
<el-icon>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-jiance"></use>
</svg>
</el-icon> 巡检
<el-icon v-if="xjLoading" class="mht-loading" style="margin-left: 4px;">
<Loading />
</el-icon>
</li>
<li class="rizhi-icon" @click.stop="openLog(item)">
<el-icon>
<ChatLineSquare />
</el-icon> 日志
</li>
<li class="edit-icon" @click.stop="handleEdit(item, index)">
<el-icon>
<Edit></Edit>
</el-icon> 编辑
</li>
<li class="delete-icon" @click.stop="handleDelete(item, index)">
<el-icon>
<Delete />
</el-icon> 删除
</li>
</ul>
</template>
</el-popover>
<!-- <span class="act-icon">
<el-icon>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-duigou3"></use>
</svg>
</el-icon>
</span> -->
</el-card>
</div>
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑机器人' : '添加机器人'" width="600px" align-center
:before-close="handleClose">
<div class="dialog-box">
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules">
<el-form-item label="机器人名称" prop="name">
<el-input v-model="ruleForm.name" placeholder="请输入机器人名称" size="large" />
</el-form-item>
<el-form-item label="机器人描述" prop="description">
<el-input type="textarea" row="3" v-model="ruleForm.description" placeholder="请输入机器人描述"
size="large" />
</el-form-item>
<el-form-item label="机器人地址" prop="server_ip" v-if="!isEdit">
<el-input v-model="ruleForm.server_ip" :readonly="isEdit" placeholder="请输入机器人地址" size="large" />
<p>示例http://210.198.123.58</p>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitRobot" :class="{ 'no-click': mhtLoading }">
<el-icon v-if="mhtLoading" class="mht-loading" style="margin-right: 4px;">
<Loading />
</el-icon>
<el-icon v-else style="margin-right: 4px;">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-baocun"></use>
</svg></el-icon>
{{ isEdit ? '保 存' : '添 加' }}
</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="dialogLog" title="机器人日志" width="1000px" align-center :before-close="handleClose">
<div class="dialog-box">
<el-table :data="logList" style="width: 100%" max-height="500px" :row-class-name="rowClassname">
<template #empty>
<span v-if="tableLoading">加载中...</span>
<span v-if="!tableLoading">暂无数据</span>
</template>
<el-table-column prop="created_at" label="监听时间" align="center" width="160">
</el-table-column>
<el-table-column prop="remark" label="监听结果" align="center" header-align="center" />
</el-table>
<div class="pagination-box">
<el-pagination v-model:current-page="logQuery.page" v-model:page-size="logQuery.page_size"
:page-sizes="[10, 20, 30, 40, 50]" :size="logQuery.page_size" background
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="dialogLog = false">
关闭
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted, reactive, nextTick } from 'vue'
import { robotList, addRobot, deleteRobot, editRobot, robotLog, robot_monitor } from '@/api/robot'
import { ElNotification } from 'element-plus';
import robotStore from '@/store/modules/robotStore'
import { useRouter } from 'vue-router';
const userRobotStore = robotStore()
const router = useRouter()
const robotCards = ref([])
const dialogVisible = ref(false)
const loading = ref(false)
const ruleFormRef = ref()
const ruleForm = ref({})
const isEdit = ref(false)
const newRobotId = ref('')
let robotCopy = {}
const rules = ref({
name: [
{
required: true,
message: '请输入机器人名称',
trigger: 'change',
}
],
description: [{
required: true,
message: '请输入机器人描述',
trigger: 'change',
}],
server_ip: [{
required: true,
message: '请输入机器人地址',
trigger: 'change',
}]
})
const query = reactive({
page: 1,
page_size: 30
})
let robtoTotal = 0
const getTable = async () => {
const res = await robotList(query)
robotCards.value = [...robotCards.value, ...res.list]
robtoTotal = res.total
}
const listLoad = () => {
console.log('到底了')
if (robtoTotal > robotCards.value.length) {
query.page++
getTable()
}
}
const handleAdd = () => {
if (robotCards.value.length > 100) {
ElNotification({
title: '提示',
message: '最多添加100个机器人',
type: 'warning',
duration: 2000
})
return
}
isEdit.value = false
dialogVisible.value = true
nextTick(() => {
ruleForm.value = {}
ruleFormRef.value.resetFields()
})
}
let editItem = {}
let editIndex = 0
const handleEdit = (item, index) => {
isEdit.value = true
editItem = item
editIndex = index
ruleForm.value = JSON.parse(JSON.stringify(item))
dialogVisible.value = true
}
const mhtLoading = ref(false)
const submitRobot = async () => {
mhtLoading.value = true
await ruleFormRef.value.validate(async (valid, fields) => {
if (valid) {
if (isEdit.value) {
try {
const res = await editRobot(ruleForm.value.id, {
description: ruleForm.value.description,
name: ruleForm.value.name,
})
editItem = ruleForm.value
robotCards.value[editIndex] = editItem
// getTable()
dialogVisible.value = false
mhtLoading.value = false
ElNotification({
type: 'success',
message: '操作成功'
})
} catch {
mhtLoading.value = false
}
} else {
try {
const res = await addRobot(ruleForm.value)
robotCards.value.push({
...ruleForm.value,
id: res.id,
head_url: res.head_url,
status: res.status,
wx_name: res.wx_name
})
// getTable()
dialogVisible.value = false
mhtLoading.value = false
ElNotification({
type: 'success',
message: '操作成功'
})
} catch {
mhtLoading.value = false
}
}
} else {
mhtLoading.value = false
}
})
}
const handleDelete = (item, index) => {
ElMessageBox.confirm(
'确认删除该机器人吗?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async () => {
ElNotification({
message: '删除中,请稍等',
type: 'success',
duration: 2000
})
await deleteRobot(item.id)
// getGroups()
robotCards.value.splice(index, 1)
})
.catch(() => {
})
}
const handleRobot = (row) => {
// newRobotId.value = row.id
userRobotStore.robotInfo = row
router.push('/dashboard')
}
const logQuery = reactive({
page: 1,
page_size: 10
})
const total = ref(0)
const dialogLog = ref(false)
const logList = ref([])
let robot_data = ''
const tableLoading = ref(false)
const openLog = async (row) => {
dialogLog.value = true
robot_data = row
tableLoading.value = true
const res = await robotLog({
robot_id: row.id,
...logQuery
})
tableLoading.value = false
logList.value = res.list
total.value = res.total
}
const handleSizeChange = (e) => {
logQuery.page = 1
logQuery.page_size = e
openLog(robot_data)
}
const handleCurrentChange = (e) => {
logQuery.page = e
openLog(robot_data)
}
const rowClassname = (e) => {
if (e.row.status == 1) {
return 'row-success'
} else {
return 'row-danger'
}
}
const xjLoading = ref(false)
const setMonitor = async (e) => {
if(xjLoading.value == true) return
try {
xjLoading.value = true
const res = await robot_monitor({
robot_id: e.id
})
e.status_text = res.status_text
e.status = res.status
xjLoading.value = false
ElNotification({
type: 'success',
message: '监听成功'
})
} catch (e) {
xjLoading.value = false
}
}
onMounted(() => {
query.page = 1
robotCards.value = []
getTable()
// newRobotId.value = userRobotStore.robotInfo.id ? userRobotStore.robotInfo.id : ''
})
</script>
<style></style>
<style lang="scss" scoped>
.group-container {
height: 100%;
.infinite-list {
height: 100%;
padding: var(--el-main-padding);
padding-right: 0;
}
.el-card {
width: 360px;
height: 160px;
cursor: pointer;
transition: all 0.3s;
position: relative;
float: left;
margin-right: 20px;
margin-bottom: 20px;
padding: 5px;
:deep(.el-card__body) {
height: 100%;
}
.robot-title {
overflow: hidden;
margin-bottom: 10px;
.robot-head {
float: left;
width: 64px;
height: 64px;
border-radius: 10px;
}
.robot-name-box {
float: left;
padding-left: 12px;
width: calc(100% - 66px);
label {
display: block;
line-height: 16px;
margin-bottom: 6px;
font-size: 14px;
}
}
}
.name {
font-size: 15px;
font-weight: bold;
margin-top: 4px;
}
.wx_name {
display: block;
color: var(--el-text-color-regular);
}
.description {
width: 86%;
font-size: 14px;
line-height: 20px;
display: -webkit-box;
-webkit-box-orient: vertical;
/* 设置垂直排列 */
-webkit-line-clamp: 2;
/* 设置行数 */
overflow: hidden;
/* 设置超出省略 */
text-overflow: ellipsis;
/* 设置省略以...结尾 */
color: var(--el-text-color-secondary);
}
.success {
position: absolute;
right: 20px;
bottom: 21px;
font-size: 12px;
color: var(--el-color-success);
&::after {
position: absolute;
left: -15px;
top: 3px;
content: '';
width: 8px;
height: 8px;
border-radius: 8px;
background-color: var(--el-color-success);
}
}
.danger {
position: absolute;
right: 20px;
bottom: 21px;
font-size: 12px;
color: var(--el-color-danger);
&::after {
position: absolute;
left: -15px;
top: 3px;
content: '';
width: 8px;
height: 8px;
border-radius: 8px;
background-color: var(--el-color-danger);
}
}
&:hover {
transform: translateY(-6px);
background-color: var(--el-color-primary-light-7);
}
.icon-robot {
font-size: 66px;
}
.act-icon {
position: absolute;
font-size: 40px;
right: -2px;
bottom: -8px;
opacity: 0;
}
}
.active-robot {
.act-icon {
opacity: 0.6;
}
}
.add-robot {
background-image: url('@/assets/images/add_robot_bg.png');
background-color: var(--el-color-primary-light-5);
background-size: 100% auto;
&:hover {
transform: translateY(-10px);
}
.add-box {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: var(--el-color-primary);
.el-icon {
position: relative;
top: -1px;
margin-right: 3px;
}
}
}
.edit-icon {
position: absolute;
right: 15px;
top: 14px;
// display: none;
height: 30px;
width: 30px;
text-align: center;
line-height: 30px;
border-radius: 8px;
transition: all 0.1s;
i {
position: relative;
left: 2px;
top: 3px;
color: var(--el-color-primary);
// color: #fff;
// transition: all 0.1s;
// transform: rotate(90deg);
}
}
}
.popover-list {
li {
height: 32px;
line-height: 32px;
padding-left: 10px;
// border-radius: 6px;
cursor: pointer;
// color: var(--el-color-primary);
.el-icon {
position: relative;
top: 2px;
margin-right: 4px;
}
&:hover {
background-color: var(--el-color-primary-light-9);
}
}
.rizhi-icon {
color: var(--el-color-warning);
}
.edit-icon {
color: var(--el-color-primary);
}
.delete-icon {
color: var(--el-color-danger);
}
}
</style>