# Conflicts:
#	ALOps_sys_backend/alops-admin/src/main/resources/application-druid.yml
#	ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/EquBase.java
#	ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquBaseMapper.xml
dev
YYD-YY 9 months ago
commit de8ba8304b
  1. 4
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/equManagement/EquBaseController.java
  2. 2
      ALOps_sys_backend/alops-admin/src/main/resources/application-druid.yml
  3. 10
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/EquBase.java
  4. 2
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/AlertLaunchServiceImpl.java
  5. 4
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/AlertRuleServiceImpl.java
  6. 1
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/EquBaseServiceImpl.java
  7. 1
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/ScheduleRulesServiceImpl.java
  8. 20
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquBaseMapper.xml
  9. 6
      ALOps_sys_fe/alops-ui/src/api/device/deviceType.js
  10. BIN
      ALOps_sys_fe/alops-ui/src/assets/images/profile.jpg
  11. 7
      ALOps_sys_fe/alops-ui/src/components/ImageUpload/index.vue
  12. 4
      ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index.vue
  13. 187
      ALOps_sys_fe/alops-ui/src/views/device/deviceType/index.vue
  14. 10
      ALOps_sys_fe/alops-ui/src/views/index.vue
  15. 185
      ALOps_sys_fe/alops-ui/src/views/index2.vue
  16. 657
      ALOps_sys_fe/alops-ui/src/views/index3.vue
  17. 1514
      ALOps_sys_sql/aiops_sys.sql

@ -129,10 +129,6 @@ public class EquBaseController extends BaseController
@GetMapping(value = "/queryimage/{id}")
@ApiOperation("查询单个设备图片")
public void getImage(@PathVariable String id, HttpServletResponse response) throws IOException {
if (id instanceof String) {
System.out.println("这是一个字符串");
}
byte[] imgBytes = equBaseService.selectEquBaseImageById(id).getEquImage();

@ -6,7 +6,7 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/alops_sys?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://localhost:3306/aiops_sys?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 1234
# 从库数据源

@ -15,7 +15,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
* 设备基本信息对象 equ_base
*
* @author ruoyi
* @date 2025-09-19
* @date 2025-09-19F
*/
@Data
public class EquBase
@ -25,6 +25,7 @@ public class EquBase
/** 设备唯一标识(设备编号) */
private String id;
/** (设备编号) */
private String equNumber;
/** 设备名称 */
@ -43,13 +44,6 @@ public class EquBase
@Excel(name = "设备厂商 id")
private String manufacture;
public EquManu getManufactureVO() {
return manufactureVO;
}
public void setManufactureVO(EquManu manufactureVO) {
this.manufactureVO = manufactureVO;
}
private EquManu manufactureVO;

@ -213,7 +213,7 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
// 3. SQL:取每台设备最新记录和次新记录,比较该字段是否变化,并关联设备类型
String sql =
"SELECT g1.*, t.name AS equ_type,b.name AS equ_name" +
"SELECT g1.*, t.name AS equ_type,b.name AS equ_name,b.equ_number" +
"FROM equ_gather g1 " +
"JOIN equ_gather g2 ON g1.equ_id = g2.equ_id " +
"JOIN equ_base b ON g1.equ_id = b.id " +

@ -140,7 +140,7 @@ public class AlertRuleServiceImpl implements IAlertRuleService
if ("equ_gather".equalsIgnoreCase(rule.getTableName())) {
// equ_gather 作为主表
sql.append("g.*, t.name AS equ_type ,b.name AS equ_name")
sql.append("g.*, t.name AS equ_type ,b.name AS equ_name,b.equ_number ")
.append("FROM equ_gather g ")
.append("JOIN equ_base b ON g.equ_id = b.id ")
.append("JOIN equ_type t ON b.equ_type = t.id ") // ★ 新增关联
@ -153,7 +153,7 @@ public class AlertRuleServiceImpl implements IAlertRuleService
.append(buildConditionsSql(conditions));
} else {
// 其他表,需要连 equ_gather → equ_base → equ_type
sql.append("src.*, g.equ_id, t.name AS equ_type,b.name AS equ_name ")
sql.append("src.*, g.equ_id, t.name AS equ_type,b.name AS equ_name,b.equ_number ")
.append("FROM ").append(rule.getTableName()).append(" src ")
.append("JOIN equ_gather g ON src.equ_gather_id = g.id ")
.append("JOIN equ_base b ON g.equ_id = b.id ")

@ -159,6 +159,7 @@ public class EquBaseServiceImpl implements IEquBaseService
if (equBase.getCreateDate() == null) {
equBase.setCreateDate(new Date());
}
equBase.setId(UUID.randomUUID().toString());
equBase.setCreateBy(SecurityUtils.getLoginUser().getUserId());
return equBaseMapper.insertEquBase(equBase);
}

@ -95,7 +95,6 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
}
// 2. 执行新规则
//首先存入规则在执行
Date now = new Date();
Long userId = SecurityUtils.getLoginUser().getUserId();

@ -25,9 +25,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="gatherFrequency" column="gather_frequency" />
<result property="equImage" column="equ_image" />
<result property="equNumber" column="equ_number" />
<result property="repairFrequency" column="repair_frequency" />
<result property="repairCost" column="repair_cost" />
<association property="manufactureVO" columnPrefix="equ_manu_" autoMapping="true" />
<association property="equTypeVO" columnPrefix="equ_type_" autoMapping="true" />
@ -43,19 +40,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="location" column="location" />
<result property="equType" column="equ_type" />
<result property="inCharge" column="in_charge" />
<result property="releaseDate" column="release_date" />
<result property="installDate" column="install_date" />
<result property="lifeCycle" column="life_cycle" />
<result property="serviceTime" column="service_time" />
<result property="warranty" column="warranty" />
<result property="createBy" column="create_by" />
<result property="createDate" column="create_date" />
<result property="updateBy" column="update_by" />
<result property="updateDate" column="update_date" />
<result property="status" column="status" />
<result property="failureFrequency" column="failure_frequency" />
<result property="repairFrequency" column="repair_frequency" />
<result property="repairCost" column="repair_cost" />
<result property="fanSpeed" column="fan_speed"/>
<result property="cpuUtilization" column="cpu_utilization"/>
<result property="memoryUtilization" column="memory_utilization"/>
@ -77,7 +71,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectEquBaseVo">
select id, name, version, ip, manufacture, location, equ_type, in_charge, install_date, life_cycle, warranty, create_by, create_date, status, device_level, supplier_id from equ_base
select id,equ_number, name, version, ip, manufacture, location, equ_type, in_charge, install_date, life_cycle, warranty, create_by, create_date, status, device_level, supplier_id from equ_base
</sql>
@ -101,6 +95,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<!-- 查询列 -->
<sql id="selectEquBaseVo2">
b.id AS id,
b.equ_number AS equNumber
b.name AS name,
b.version AS version,
b.ip AS ip,
@ -117,6 +112,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
b.status AS status,
b.device_level AS deviceLevel,
g.health AS health
</sql>
<select id="selectEquBaseList" parameterType="equBaseQueryDto" resultType="com.alops.system.domain.vo.queryEquBaseVO">
@ -337,6 +333,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="warranty != null">warranty,</if>
<if test="createBy != null">create_by,</if>
<if test="createDate != null">create_date,</if>
<if test="equNumber != null">equ_number,</if>
status,
<if test="equImage != null">equ_image,</if>
@ -359,7 +356,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="warranty != null">#{warranty},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createDate != null">#{createDate},</if>
<if test="equNumber != null">#{equNumber},</if>
COALESCE(#{status,jdbcType=INTEGER}, 0),
<if test="equImage != null">#{equImage,jdbcType=BLOB},</if>
COALESCE(#{failureFrequency,jdbcType=BIGINT}, 0),
@ -387,6 +384,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="deviceLevel != null">device_level = #{deviceLevel},</if>
<if test="supplierId != null">supplier_id = #{supplierId},</if>
<if test="equImage != null">equ_image = #{equImage},</if>
<if test="equNumber != null">equ_number = #{equNumber},</if>
</trim>
where id = #{id}
</update>

@ -1,6 +1,6 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
const api = '/equipmentType/'
const api = '/device/deviceType/'
// 查询详细
export function getDict() {
@ -13,7 +13,7 @@ export function getDict() {
// 查询列表
export function list(query) {
return request({
url: api + 'queryByVO',
url: api + 'query',
method: 'get',
params: query
})
@ -40,7 +40,7 @@ export function update(data) {
// 删除
export function del(id) {
return request({
url: api + 'delete' + id,
url: api + id,
method: 'delete'
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 38 KiB

@ -64,7 +64,7 @@ export default {
//
limit: {
type: Number,
default: 5
default: 1
},
// (MB)
fileSize: {
@ -195,8 +195,9 @@ export default {
},
//
handleUploadSuccess(res, file) {
console.log("%c 🍍: handleUploadSuccess -> res ", "font-size:16px;background-color:#dca159;color:white;", res)
if (res.code === 200) {
this.uploadList.push({ name: res.fileName, url: res.fileName })
this.uploadList.push({ name: res.fileName, url: res.url })
this.uploadedSuccessfully()
} else {
this.number--
@ -205,6 +206,8 @@ export default {
this.$refs.imageUpload.handleRemove(file)
this.uploadedSuccessfully()
}
console.log("%c 🇸🇰: handleUploadSuccess -> this.fileList ", "font-size:16px;background-color:#6a826e;color:white;", this.fileList)
},
//
handleDelete(file) {

@ -4,7 +4,7 @@
<el-card class="customer-service">
<!-- 客服头部 -->
<div class="service-header">
<h2>智能客服</h2>
<h2>AI智能助手</h2>
<p>您好我是智能助手很高兴为您服务</p>
</div>
@ -72,7 +72,7 @@ export default {
newMessage: '',
messages: [
{
text: '您好!我是智能客服,请问有什么可以帮您?',
text: '您好!我是AI智能助手,请问有什么可以帮您?',
sender: 'service',
time: this.formatTime(new Date())
}

@ -11,14 +11,6 @@
<el-form-item label="类型名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入类型名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
</el-form-item>
<!-- <el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="类型状态" clearable style="width: 240px">
<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item> -->
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@ -29,21 +21,12 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['device:deviceType:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['device:deviceType:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['device:deviceType:import']">导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['device:deviceType:export']">导出</el-button>
</el-col>
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar> -->
</el-row>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="类型编号" align="center" key="Id" prop="Id"/>
<el-table-column label="类型编号" align="center" key="id" prop="id"/>
<el-table-column label="设备类型名称" align="center" key="name" prop="name" :show-overflow-tooltip="true" />
<el-table-column label="描述" align="center" key="description" prop="description" :show-overflow-tooltip="true" />
<el-table-column label="图片" align="center" key="image" prop="image" :show-overflow-tooltip="true" >
@ -51,14 +34,14 @@
<ImagePreview :src="scope.row.image"/>
</template>
</el-table-column>
<el-table-column label="创建人" align="center" key="CreateBy" prop="CreateBy" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160">
<el-table-column label="创建人" align="center" key="createBy" prop="createBy" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createDate" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
<span>{{ parseTime(scope.row.createDate) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope" v-if="scope.row.Id !== 1">
<template slot-scope="scope" v-if="scope.row.id !== 1">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['device:deviceType:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:deviceType:remove']">删除</el-button>
</template>
@ -71,7 +54,7 @@
</splitpanes>
</el-row>
<el-dialog :title="title" :visible.sync="open" width="760px" append-to-body>
<el-dialog :title="title" :visible.sync="open" width="760px" append-to-body :destroy-on-close="true">
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
<el-row :gutter="20">
<!-- 类型昵称 + 手机号码 -->
@ -80,15 +63,15 @@
<el-input v-model="form.name" placeholder="请输入类型昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备图片" prop="phonenumber">
<ImageUpload/>
<el-col :span="24">
<el-form-item label="设备图片" prop="image">
<ImageUpload @input="upImage" :value="form.image"/>
</el-form-item>
</el-col>
<!-- 备注单独一行 -->
<el-col :span="24">
<el-form-item label="描述">
<el-form-item label="介绍">
<el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入内容" />
</el-form-item>
</el-col>
@ -102,30 +85,11 @@
</span>
</el-dialog>
<!-- 类型导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的类型数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/system/user"
import { list, getUser, del, add, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/device/deviceType"
import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
@ -138,6 +102,7 @@ export default {
components: { Treeselect, Splitpanes, Pane },
data() {
return {
appBase:process.env.VUE_APP_BASE_API,
//
loading: true,
//
@ -195,21 +160,8 @@ export default {
queryParams: {
pageNum: 1,
pageSize: 10,
name: undefined,
phonenumber: undefined,
status: undefined,
deptId: undefined
name: undefined
},
//
columns: [
{ key: 0, label: '类型编号', visible: true, showInSelector: true },
{ key: 1, label: '类型名称', visible: true, showInSelector: true },
{ key: 2, label: '类型昵称', visible: true, showInSelector: true },
{ key: 3, label: '部门', visible: false, showInSelector: false }, // 👈
{ key: 4, label: '手机号码', visible: true, showInSelector: true },
{ key: 5, label: '状态', visible: true, showInSelector: true },
{ key: 6, label: '创建时间', visible: true, showInSelector: true }
],
//
rules: {
@ -217,28 +169,9 @@ export default {
{ required: true, message: "类型名称不能为空", trigger: "blur" },
{ min: 2, max: 20, message: '类型名称长度必须介于 2 和 20 之间', trigger: 'blur' }
],
name: [
{ required: true, message: "类型昵称不能为空", trigger: "blur" }
],
password: [
{ required: true, message: "类型密码不能为空", trigger: "blur" },
{ min: 5, max: 20, message: '类型密码长度必须介于 5 和 20 之间', trigger: 'blur' },
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
],
email: [
{
type: "email",
message: "请输入正确的邮箱地址",
trigger: ["blur", "change"]
}
image: [
{ required: true, message: "请上传设备图片", trigger: "blur" },
],
phonenumber: [
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur"
}
]
}
}
},
@ -259,12 +192,18 @@ export default {
/** 查询类型列表 */
getList() {
this.loading = true
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
list(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
this.userList = response.rows
this.total = response.total
this.loading = false
}
)
},
upImage(fileList){
console.log(fileList)
this.form.image = fileList
},
// /** */
// getDeptTree() {
@ -299,7 +238,7 @@ export default {
handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用"
this.$modal.confirm('确认要"' + text + '""' + row.name + '"类型吗?').then(function() {
return changeUserStatus(row.Id, row.status)
return changeUserStatus(row.id, row.status)
}).then(() => {
this.$modal.msgSuccess(text + "成功")
}).catch(function() {
@ -314,18 +253,12 @@ export default {
//
reset() {
this.form = {
Id: undefined,
deptId: undefined,
name: undefined,
id: undefined,
name: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: "0",
description: undefined,
postIds: [],
roleIds: []
createBy: undefined,
image: undefined,
createDate: undefined
}
this.resetForm("form")
},
@ -344,85 +277,35 @@ export default {
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.Id)
this.ids = selection.map(item => item.id)
this.single = selection.length != 1
this.multiple = !selection.length
},
//
handleCommand(command, row) {
switch (command) {
case "handleResetPwd":
this.handleResetPwd(row)
break
case "handleAuthRole":
this.handleAuthRole(row)
break
default:
break
}
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
getUser().then(response => {
this.postOptions = response.posts
this.roleOptions = response.roles
this.open = true
this.title = "添加类型"
this.form.password = this.initPassword
})
this.open = true
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const Id = row.Id || this.ids
getUser(Id).then(response => {
this.form = response.data
this.postOptions = response.posts
this.roleOptions = response.roles
this.$set(this.form, "postIds", response.postIds)
this.$set(this.form, "roleIds", response.roleIds)
const id = row.id || this.ids
this.open = true
this.title = "修改类型"
this.form.password = ""
})
},
/** 重置密码按钮操作 */
handleResetPwd(row) {
this.$prompt('请输入"' + row.name + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: "类型密码长度必须介于 5 和 20 之间",
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return "不能包含非法字符:< > \" ' \\\ |"
}
},
}).then(({ value }) => {
resetUserPwd(row.Id, value).then(response => {
this.$modal.msgSuccess("修改成功,新密码是:" + value)
})
}).catch(() => {})
},
/** 分配角色操作 */
handleAuthRole: function(row) {
const Id = row.Id
this.$router.push("/system/user-auth/role/" + Id)
this.form = {...row, image: process.env.VUE_APP_BASE_API+row.image}
},
/** 提交按钮 */
submitForm: function() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.Id != undefined) {
if (this.form.id != undefined) {
updateUser(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addUser(this.form).then(response => {
add(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
@ -433,9 +316,9 @@ export default {
},
/** 删除按钮操作 */
handleDelete(row) {
const Ids = row.Id || this.ids
const Ids = row.id || this.ids
this.$modal.confirm('是否确认删除类型编号为"' + Ids + '"的数据项?').then(function() {
return delUser(Ids)
return del(Ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")

@ -52,7 +52,7 @@
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<div class="chart-wrapper overflow-auto">
<div class="card-title"><i class="el-icon-monitor"></i>设备预警信息</div>
<div class="div0">
@ -277,6 +277,14 @@ export default {
list-style-type: none;
padding: 0;
}
.overflow-auto {
overflow: auto;
}
.alert-list {
background-color: #fff;
padding: 16px;
margin-bottom: 32px;
}
.alert-list .div1 {
display: flex;

@ -0,0 +1,185 @@
<template>
<div class="dashboard">
<h1>唐山烟草智能网络设备运维与问答系统</h1>
<el-row :gutter="20">
<!-- 设备状态监控 -->
<el-col :span="12">
<el-card>
<h2>设备状态监控</h2>
<!-- 这里可以添加设备状态监控的具体内容例如表格或图表 -->
<el-table :data="deviceStatusData" style="width: 100%">
<el-table-column prop="name" label="设备名称"></el-table-column>
<el-table-column prop="location" label="设备位置"></el-table-column>
<el-table-column prop="cpuUsage" label="CPU使用率"></el-table-column>
<el-table-column prop="memoryUsage" label="内存使用率"></el-table-column>
<el-table-column prop="status" label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '正常' ? 'success' : 'danger'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<!-- 设备状态统计图表 -->
<el-col :span="12">
<el-card>
<h2>检测设备信息 (按状态统计饼状图)</h2>
<!-- 这里可以使用echarts或其他图表库来展示饼状图 -->
<div id="statusChart" style="width: 100%; height: 300px;"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<h2>检测设备信息 (按设备类型饼状图)</h2>
<div id="typeChart" style="width: 100%; height: 300px;"></div>
</el-card>
</el-col>
<!-- 设备预警信息 -->
<el-col :span="12">
<el-card>
<h2>设备预警信息</h2>
<el-table :data="alertData" style="width: 100%">
<el-table-column prop="time" label="预警时间"></el-table-column>
<el-table-column prop="device" label="设备名称"></el-table-column>
<el-table-column prop="location" label="设备位置"></el-table-column>
<el-table-column prop="level" label="预警等级">
<template slot-scope="scope">
<el-tag :type="getTagType(scope.row.level)">
{{ scope.row.level }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<!-- 预警信息统计 -->
<el-col :span="12">
<el-card>
<h2>预警信息统计</h2>
<div id="alertStatsChart" style="width: 100%; height: 300px;"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import * as echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
export default {
data() {
return {
deviceStatusData: [
//
{ name: '设备1', location: '位置1', cpuUsage: '30%', memoryUsage: '40%', status: '正常' },
{ name: '设备2', location: '位置2', cpuUsage: '70%', memoryUsage: '80%', status: '异常' },
],
alertData: [
//
{ time: '2023-10-01 10:00:00', device: '设备2', location: '位置2', level: '高' },
{ time: '2023-10-01 11:00:00', device: '设备3', location: '位置3', level: '中' },
],
};
},
mounted() {
this.initCharts();
},
methods: {
getTagType(level) {
const types = { '高': 'danger', '中': 'warning', '低': 'success' };
return types[level] || 'info';
},
initCharts() {
//
const statusChart = echarts.init(document.getElementById('statusChart'));
statusChart.setOption({
title: { text: '设备状态统计', subtext: '按状态', left: 'center' },
tooltip: { trigger: 'item' },
series: [
{
name: '设备状态',
type: 'pie',
radius: '50%',
data: [
{ value: 44, name: '自主房屋' },
{ value: 33, name: '出租房屋' },
{ value: 11, name: '空置房屋' },
{ value: 11, name: '其他' },
],
},
],
});
//
const typeChart = echarts.init(document.getElementById('typeChart'));
typeChart.setOption({
title: { text: '设备类型统计', subtext: '按设备类型', left: 'center' },
tooltip: { trigger: 'item' },
series: [
{
name: '设备类型',
type: 'pie',
radius: '50%',
data: [
{ value: 44, name: '自主房屋' },
{ value: 33, name: '出租房屋' },
{ value: 11, name: '空置房屋' },
{ value: 11, name: '其他' },
],
},
],
});
//
const alertStatsChart = echarts.init(document.getElementById('alertStatsChart'));
alertStatsChart.setOption({
title: { text: '预警信息统计', left: 'center' },
tooltip: {},
radar: {
indicator: [
{ name: '火警类', max: 500 },
{ name: '冲禁类', max: 500 },
{ name: '灾害', max: 500 },
{ name: '群体性', max: 500 },
{ name: '行政类', max: 500 },
{ name: '交通类', max: 500 },
{ name: '重大', max: 500 },
],
},
series: [{
name: '预警统计',
type: 'radar',
data: [
{
value: [75, 452, 64, 94, 44, 0, 29],
name: '预警次数',
},
],
}],
});
},
},
};
</script>
<style>
.dashboard {
padding: 20px;
background-color: #f0f2f5;
min-height: 100vh;
}
h1, h2 {
text-align: center;
}
.el-card {
margin-bottom: 20px;
}
</style>

@ -0,0 +1,657 @@
<template>
<div class="dashboard-container">
<div class="dashboard-body">
<!-- 左侧面板 -->
<div class="left-panel">
<!-- 数据概览 -->
<div class="dashboard-card">
<div class="card-title"><i class="el-icon-data-line"></i>预警信息统计</div>
<div class="data-overview">
<div class="data-item">
<div class="data-label">预警总数</div>
<digital :configData="{number:[111]}" />
</div>
<div class="data-item">
<div class="data-label">预警总数</div>
<digital :configData="{number:[111]}" />
</div>
<div class="data-item">
<div class="data-label">预警总数</div>
<digital :configData="{number:[111]}" />
</div>
<div class="data-item">
<div class="data-label">预警总数</div>
<digital :configData="{number:[111]}" />
</div>
</div>
</div>
<!-- 柱状图 -->
<div class="dashboard-card">
<div class="card-title"><i class="el-icon-s-data"></i> 设备状态监控</div>
<div class="chart-container">
<dv-scroll-board :config="scrollConfig" style="width:500px;height:280px" />
</div>
</div>
</div>
<!-- 中间地图面板 -->
<div class="map-panel">
<div class="dashboard-card" style="flex: 1;">
<div class="card-title"><i class="el-icon-map-location"></i> 智慧城市地理分布</div>
<div class="map-container" ref="mapChart"></div>
</div>
</div>
<!-- 右侧面板 -->
<div class="right-panel">
<!-- 实时数据 -->
<div class="dashboard-card">
<div class="card-title"><i class="el-icon-monitor"></i> 实时监测</div>
<div class="data-overview">
<div class="data-item">
<div class="data-label">交通流量</div>
<div class="data-value">86,542</div>
<div class="data-trend"> 12.5%</div>
</div>
<div class="data-item">
<div class="data-label">能耗指数</div>
<div class="data-value">2,356</div>
<div class="data-trend down"> 3.2%</div>
</div>
<div class="data-item">
<div class="data-label">空气质量</div>
<div class="data-value">优良</div>
<div class="data-trend"> 稳定</div>
</div>
<div class="data-item">
<div class="data-label">治安指数</div>
<div class="data-value">98.7%</div>
<div class="data-trend"> 0.5%</div>
</div>
</div>
</div>
<!-- 饼图 -->
<div class="dashboard-card">
<div class="card-title"><i class="el-icon-pie-chart"></i> 人口结构</div>
<div class="chart-container" ref="populationChart"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import digital from './dashboard/digital'
export default {
components: {
digital
},
data() {
return {
scrollConfig:{
header: ['设备名称', '设备位置', 'CPU使用率','内存使用率'],
data: [
['行1列1', '行1列2', '行1列3', '行1列4', '行1列5', '行1列6','行1列7', '行1列8'],
['行2列1', '行2列2', '行2列3', '行2列4', '行2列5', '行2列6','行2列7', '行2列8'],
['行3列1', '行3列2', '行3列3', '行3列4', '行3列5', '行3列6','行3列7', '行3列8'],
['行4列1', '行4列2', '行4列3', '行4列4', '行4列5', '行4列6','行4列7', '行4列8'],
['行5列1', '行5列2', '行5列3', '行5列4', '行5列5', '行5列6','行5列7', '行5列8'],
['行6列1', '行6列2', '行6列3', '行6列4', '行6列5', '行6列6','行6列7', '行6列8'],
['行7列1', '行7列2', '行7列3', '行7列4', '行7列5', '行7列6','行7列7', '行7列8'],
['行8列1', '行8列2', '行8列3', '行8列4', '行8列5', '行8列6','行8列7', '行8列8'],
['行9列1', '行9列2', '行9列3', '行9列4', '行9列5', '行9列6','行9列7', '行9列8'],
['行10列1', '行10列2', '行10列3', '行10列4', '行10列5', '行10列6','行10列7', '行10列8']
]
},
currentTime: '',
currentDate: '',
districtRanking: [
{ name: '高新区', value: 856 },
{ name: '经济开发区', value: 732 },
{ name: '西湖区', value: 698 },
{ name: '东海区', value: 645 },
{ name: '滨江区', value: 587 }
],
industryChart: null,
populationChart: null,
economyChart: null,
mapChart: null
};
},
mounted() {
this.updateTime();
setInterval(this.updateTime, 1000);
this.$nextTick(() => {
this.initCharts();
});
//
window.addEventListener('resize', this.resizeCharts);
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeCharts);
},
methods: {
formatter1 (number) {
const numbers = number.toString().split('').reverse()
const segs = []
while (numbers.length) segs.push(numbers.splice(0, 3).join(''))
return segs.join(',').split('').reverse().join('')
},
updateTime() {
const now = new Date();
this.currentTime = now.toLocaleTimeString();
this.currentDate = now.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long'
});
},
initCharts() {
//
this.industryChart = echarts.init(this.$refs.industryChart);
this.industryChart.setOption({
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
borderColor: '#4e9ff7',
textStyle: {
color: '#fff'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['高新技术', '制造业', '金融业', '服务业', '农业'],
axisLine: {
lineStyle: {
color: '#5d92d0'
}
},
axisLabel: {
color: '#3d3d3d'
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#5d92d0'
}
},
axisLabel: {
color: '#3d3d3d'
},
splitLine: {
lineStyle: {
color: 'rgba(93, 146, 208, 0.2)'
}
}
},
series: [
{
name: '产值(亿元)',
type: 'bar',
data: [320, 185, 195, 150, 85],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#4e9ff7' },
{ offset: 1, color: '#8bd6ff' }
])
},
label: {
show: true,
position: 'top',
color: '#3d3d3d'
}
}
]
});
//
this.populationChart = echarts.init(this.$refs.populationChart);
this.populationChart.setOption({
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
borderColor: '#4e9ff7',
textStyle: {
color: '#fff'
}
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
textStyle: {
color: '#3d3d3d'
}
},
series: [
{
name: '人口结构',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#0a1d35',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '14',
fontWeight: 'bold',
color: '#fff'
}
},
labelLine: {
show: false
},
data: [
{ value: 245, name: '0-18岁' },
{ value: 520, name: '19-35岁' },
{ value: 395, name: '36-60岁' },
{ value: 198, name: '60岁以上' }
],
color: ['#4e9ff7', '#5d92d0', '#6b88b2', '#7a7e95']
}
]
});
// 线
this.economyChart = echarts.init(this.$refs.economyChart);
this.economyChart.setOption({
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
borderColor: '#4e9ff7',
textStyle: {
color: '#fff'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
axisLine: {
lineStyle: {
color: '#5d92d0'
}
},
axisLabel: {
color: '#3d3d3d'
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#5d92d0'
}
},
axisLabel: {
color: '#3d3d3d',
formatter: '{value} 亿'
},
splitLine: {
lineStyle: {
color: 'rgba(93, 146, 208, 0.2)'
}
}
},
series: [
{
name: 'GDP增长',
type: 'line',
smooth: true,
data: [320, 332, 301, 334, 390, 330, 320, 342, 361, 384, 410, 432],
itemStyle: {
color: '#4e9ff7'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(78, 159, 247, 0.6)' },
{ offset: 1, color: 'rgba(78, 159, 247, 0.1)' }
])
},
markPoint: {
data: [
{ type: 'max', name: '最高值' },
{ type: 'min', name: '最低值' }
]
}
}
]
});
//
this.mapChart = echarts.init(this.$refs.mapChart);
this.mapChart.setOption({
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
borderColor: '#4e9ff7',
textStyle: {
color: '#fff'
},
formatter: function(params) {
return `${params.name}<br/>发展指数: ${params.value}`;
}
},
visualMap: {
min: 0,
max: 1000,
text: ['高', '低'],
realtime: false,
calculable: true,
inRange: {
color: ['#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#fee090', '#fdae61', '#f46d43', '#d73027']
},
textStyle: {
color: '#3d3d3d'
}
},
series: [
{
name: '发展指数',
type: 'map',
map: 'china',
emphasis: {
label: {
show: true,
color: '#fff'
},
itemStyle: {
areaColor: '#3498db'
}
},
data: [
{ name: '北京', value: 1000 },
{ name: '天津', value: 800 },
{ name: '上海', value: 950 },
{ name: '重庆', value: 700 },
{ name: '河北', value: 600 },
{ name: '山西', value: 550 },
{ name: '辽宁', value: 650 },
{ name: '吉林', value: 580 },
{ name: '江苏', value: 850 },
{ name: '浙江', value: 820 },
{ name: '安徽', value: 630 },
{ name: '福建', value: 720 },
{ name: '江西', value: 590 },
{ name: '山东', value: 780 },
{ name: '河南', value: 680 },
{ name: '湖北', value: 710 },
{ name: '湖南', value: 690 },
{ name: '广东', value: 900 },
{ name: '海南', value: 620 },
{ name: '四川', value: 730 },
{ name: '贵州', value: 580 },
{ name: '云南', value: 610 },
{ name: '陕西', value: 670 },
{ name: '甘肃', value: 540 },
{ name: '青海', value: 500 },
{ name: '台湾', value: 750 },
{ name: '内蒙古', value: 560 },
{ name: '广西', value: 610 },
{ name: '宁夏', value: 530 },
{ name: '新疆', value: 550 },
{ name: '西藏', value: 480 },
{ name: '香港', value: 880 },
{ name: '澳门', value: 780 },
{ name: '黑龙江', value: 610 }
],
itemStyle: {
areaColor: 'rgba(93, 146, 208, 0.3)',
borderColor: '#5d92d0',
borderWidth: 1
}
}
]
});
},
resizeCharts() {
if (this.industryChart) this.industryChart.resize();
if (this.populationChart) this.populationChart.resize();
if (this.economyChart) this.economyChart.resize();
if (this.mapChart) this.mapChart.resize();
},
refreshData() {
this.$message({
message: '数据刷新中...',
type: 'info',
customClass: 'message-box'
});
//
setTimeout(() => {
this.$message({
message: '数据已更新到最新状态',
type: 'success',
customClass: 'message-box'
});
//
this.initCharts();
}, 1000);
}
}
}
</script>
<style lang="scss" scoped>
.dashboard-container {
width: 100%;
// height: 100vh;
padding: 20px;
display: flex;
flex-direction: column;
}
.dashboard-body {
flex: 1;
display: flex;
gap: 20px;
}
.left-panel, .right-panel {
width: 35%;
display: flex;
flex-direction: column;
gap: 20px;
}
.map-panel {
flex: 1;
display: flex;
flex-direction: column;
}
.dashboard-card {
border-radius: 10px;
box-shadow: 0 4px 25px rgba(0, 0, 0, 0.4);
padding: 15px;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
border: 1px solid rgba(100, 180, 246, 0.15);
backdrop-filter: blur(5px);
}
// .dashboard-card::before {
// content: '';
// position: absolute;
// top: 0;
// left: 0;
// right: 0;
// height: 3px;
// background: linear-gradient(90deg, #4e9ff7, #8bd6ff);
// }
.card-title {
font-size: 16px;
margin-bottom: 15px;
display: flex;
align-items: center;
color: #3d3d3d;
font-weight: bold;
}
.card-title i {
margin-right: 8px;
font-size: 18px;
}
.chart-container {
flex: 1;
width: 100%;
min-height: 200px;
}
.map-container {
flex: 1;
width: 100%;
border-radius: 8px;
overflow: hidden;
}
.data-overview {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.data-item {
flex: 1;
min-width: 45%;
text-align: center;
padding: 15px 10px;
background: rgba(38, 84, 139, 0.3);
border-radius: 8px;
transition: all 0.3s ease;
}
.data-item:hover {
background: rgba(38, 84, 139, 0.5);
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.data-value {
font-size: 24px;
font-weight: bold;
margin: 10px 0;
color: #3d3d3d;
}
.data-label {
font-size: 14px;
color: #3d3d3d;
}
.data-trend {
font-size: 12px;
color: #3d3d3d;
}
.data-trend.down {
color: #3d3d3d;
}
.footer {
text-align: center;
padding: 15px;
color: #3d3d3d;
font-size: 14px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
height: 50px;
}
/* 动画效果 */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.dashboard-card {
animation: fadeIn 0.8s ease-out;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.left-panel, .right-panel {
width: 30%;
}
}
@media (max-width: 992px) {
.dashboard-body {
flex-direction: column;
}
.left-panel, .right-panel {
width: 100%;
flex-direction: row;
}
.dashboard-card {
flex: 1;
}
.map-panel {
order: -1;
height: 400px;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: rgba(78, 159, 247, 0.5);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(78, 159, 247, 0.7);
}
</style>

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save