锋哥写一套前后端分离Python权限系统 基于Django5+DRF+Vue3.2+Element Plus+Jwt 视频教程 ,帅呆了~~

news2024/11/26 0:26:32

大家好,我是java1234_小锋老师,最近写了一套【前后端分离Python权限系统 基于Django5+DRF+Vue3.2+Element Plus+Jwt 】视频教程,持续更新中,计划月底更新完,感谢支持。

视频在线地址:

打造前后端分离Python权限系统 基于Django5+DRF+Vue3.2+Element Plus+Jwt 视频教程 (火爆连载更新中..)_哔哩哔哩_bilibili

项目介绍

本课程采用主流的Python技术栈实现,Mysql8数据库,Django5后端,redis缓存,DRF框架 ,Vue3.2+Element Plus实现后台管理。基于JWT技术实现前后端分离。

业务功能实现了,登录验证鉴权功能,用户管理(增删改查,角色授权),角色管理(增删改查,权限分配),权限管理(增删改查)。

系统展示

部分代码

import base64
import copy
import hashlib
import json
import os
import random
import uuid
from datetime import datetime
from io import BytesIO

from captcha.image import ImageCaptcha
from django.core import serializers
from django.core.cache import cache
from django.core.paginator import Paginator
from django.core.serializers import serialize
from django.http import JsonResponse, QueryDict
from django.views import View

from rest_framework_jwt.settings import api_settings

from python222_admin import settings
from user.models import SysUser, SysUserSerializer, SysRole, SysMenu, SysMenuSerializer, SysUserRole


# Create your views here.
class LoginView(View):

    # 构造菜单树
    def buildTreeMenu(self, sysMenuList):
        resultMenuList: list[SysMenu] = list()
        for menu in sysMenuList:
            # 寻找子节点
            for e in sysMenuList:
                if e.parent_id == menu.id:
                    if not hasattr(menu, "children"):
                        menu.children = list()
                    menu.children.append(e)
            # 判断父节点,添加到集合
            if menu.parent_id == 0:
                resultMenuList.append(menu)
        return resultMenuList

    def post(self, request):
        # data = json.loads(request.body.decode("utf-8"))
        username = request.GET.get('username')
        password = request.GET.get('password')
        code = request.GET.get('code')  # 用户填写的验证码
        uuid = request.GET.get('uuid')
        token = ''
        print(password, hashlib.md5(password.encode()).hexdigest())
        captcha = cache.get(uuid)
        print("captcha", captcha)
        if captcha == '' or captcha.lower() != code.lower():  # 判断验证码
            return JsonResponse({'code': 500, 'info': '验证码错误!'})
        try:
            user = SysUser.objects.get(username=username, password=hashlib.md5(password.encode()).hexdigest())
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            # 将用户对象传递进去,获取到该对象的属性值
            payload = jwt_payload_handler(user)
            # 将属性值编码成jwt格式的字符串
            token = jwt_encode_handler(payload)
            # roleList = SysRole.objects.raw("select id,role_id from sys_user_role where user_id=" + str(user.id))
            roleList = SysRole.objects.raw(
                "select id,name from sys_role where id in (select role_id from sys_user_role where user_id=" + str(
                    user.id) + ")")
            menuSet: set[SysMenu] = set()
            # roles:str = ""  # 当前用户拥有的角色,逗号隔开
            roles = ",".join([role.name for role in roleList])

            print("roles=", roles)
            for row in roleList:
                # print("role_id:" + str(row.role_id))

                menuList = SysMenu.objects.raw(
                    "select * from sys_menu where id in (select menu_id from sys_role_menu where role_id=" + str(
                        row.id) + ")")
                for row2 in menuList:
                    # print(row2.menu_id)
                    menuSet.add(row2)
            menuList: list[SysMenu] = list(menuSet)  # set转list
            print(menuList)
            sorted_menuList = sorted(menuList)  # 根据order_num排序
            # 构造菜单树
            sysMenuList: list[SysMenu] = self.buildTreeMenu(sorted_menuList)
            print(sysMenuList)
            serializerMenuList: list[SysMenuSerializer] = list()
            for sysMenu in sysMenuList:
                serializerMenuList.append(SysMenuSerializer(sysMenu).data)


        except Exception as e:
            print(e)
            return JsonResponse({'code': 500, 'info': '用户名或者密码错误!'})
        return JsonResponse(
            {'code': 200, 'user': SysUserSerializer(user).data, 'token': token, 'roles': roles,
             'menuList': serializerMenuList})


class InfoView(View):

    def get(self, request):
        token = request.META.get('HTTP_AUTHORIZATION')
        jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
        print("result=", jwt_decode_handler(token))


# 用户角色授权
class GrantRole(View):

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        user_id = data['id']
        roleIdList = data['roleIds']
        print(user_id, roleIdList)
        SysUserRole.objects.filter(user_id=user_id).delete()  # 删除用户角色关联表中的指定用户数据
        for roleId in roleIdList:
            userRole = SysUserRole(user_id=user_id, role_id=roleId)
            userRole.save()
        return JsonResponse({'code': 200})


class PwdView(View):

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        id = data['id']
        oldPassword = data['oldPassword']
        newPassword = data['newPassword']
        obj_user = SysUser.objects.get(id=id)
        if obj_user.password == hashlib.md5(oldPassword.encode()).hexdigest():
            obj_user.password = hashlib.md5(newPassword.encode()).hexdigest()
            obj_user.update_time = datetime.now().date()
            obj_user.save()
            return JsonResponse({'code': 200})
        else:
            return JsonResponse({'code': 500, 'errorInfo': '原密码错误!'})


class SaveView(View):

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        if data['id'] == -1:  # 添加
            obj_sysUser = SysUser(username=data['username'], password=data['password'],
                                  email=data['email'], phonenumber=data['phonenumber'],
                                  status=data['status'],
                                  remark=data['remark'])
            obj_sysUser.create_time = datetime.now().date()
            obj_sysUser.avatar = 'default.jpg'
            obj_sysUser.password = hashlib.md5("123456".encode()).hexdigest()
            obj_sysUser.save()
        else:  # 修改
            obj_sysUser = SysUser(id=data['id'], username=data['username'], password=data['password'],
                                  avatar=data['avatar'], email=data['email'], phonenumber=data['phonenumber'],
                                  login_date=data['login_date'], status=data['status'], create_time=data['create_time'],
                                  update_time=data['update_time'], remark=data['remark'])
            obj_sysUser.update_time = datetime.now().date()
            obj_sysUser.save()
        return JsonResponse({'code': 200})


# 用户状态修改
class StatusView(View):

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        id = data['id']
        status = data['status']
        user_object = SysUser.objects.get(id=id)
        user_object.status = status
        user_object.save()
        return JsonResponse({'code': 200})


# 重置密码
class PasswordView(View):

    def get(self, request):
        id = request.GET.get("id")
        user_object = SysUser.objects.get(id=id)
        user_object.password = hashlib.md5("123456".encode()).hexdigest()
        user_object.update_time = datetime.now().date()
        user_object.save()
        return JsonResponse({'code': 200})


class TestView(View):

    def post(self, request):
        return JsonResponse({'code': 200, 'info': '测试!'})


# 用户名验证
class CheckView(View):

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        username = data['username']
        print("username=", username)
        if SysUser.objects.filter(username=username).exists():
            return JsonResponse({'code': 500})
        else:
            return JsonResponse({'code': 200})


# 用户基本操作
class ActionView(View):

    def get(self, request):
        """
        根据id获取用户信息
        :param request:
        :return:
        """
        id = request.GET.get("id")
        user_object = SysUser.objects.get(id=id)
        return JsonResponse({'code': 200, 'user': SysUserSerializer(user_object).data})

    def delete(self, request):
        """
        删除操作
        :param request:
        :return:
        """
        idList = json.loads(request.body.decode("utf-8"))
        SysUserRole.objects.filter(user_id__in=idList).delete()
        SysUser.objects.filter(id__in=idList).delete()
        return JsonResponse({'code': 200})


# 用户信息查询
class SearchView(View):

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        pageNum = data['pageNum']  # 当前页
        pageSize = data['pageSize']  # 每页大小
        query = data['query']  # 查询参数
        print(pageSize, pageNum)
        userListPage = Paginator(SysUser.objects.filter(username__icontains=query), pageSize).page(pageNum)
        obj_users = userListPage.object_list.values()  # 转成字典
        users = list(obj_users)  # 把外层的容器转为List
        for user in users:
            userId = user['id']
            # roleList = SysRole.objects.raw("select id,user_id,role_id from sys_user_role where user_id=" + str(userId))
            roleList = SysRole.objects.raw(
                "select id,name from sys_role where id in (select role_id from sys_user_role where user_id=" + str(
                    userId) + ")")
            roles = ",".join([role.name for role in roleList])
            roleListDict = []
            for role in roleList:
                roleDict = {}
                roleDict['id'] = role.id
                roleDict['name'] = role.name
                roleListDict.append(roleDict)

            user['roleList'] = roleListDict
        total = SysUser.objects.filter(username__icontains=query).count()
        return JsonResponse(
            {'code': 200, 'userList': users, 'total': total})


class AvatarView(View):

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        id = data['id']
        avatar = data['avatar']
        obj_user = SysUser.objects.get(id=id)
        obj_user.avatar = avatar
        obj_user.save()
        return JsonResponse({'code': 200})


class ImageView(View):

    def post(self, request):
        file = request.FILES.get('avatar')
        print("file:", file)
        if file:
            file_name = file.name
            suffixName = file_name[file_name.rfind("."):]
            new_file_name = datetime.now().strftime('%Y%m%d%H%M%S') + suffixName
            file_path = str(settings.MEDIA_ROOT) + "\\userAvatar\\" + new_file_name
            print("file_path:", file_path)
            try:
                with open(file_path, 'wb') as f:
                    for chunk in file.chunks():
                        f.write(chunk)
                return JsonResponse({'code': 200, 'title': new_file_name})
            except:
                return JsonResponse({'code': 500, 'errorInfo': '上传头像失败'})


class CaptchaView(View):

    def get(self, request):
        characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        data = ''.join(random.sample(characters, 4))
        print("data", data)
        captcha = ImageCaptcha()
        imageData: BytesIO = captcha.generate(data)
        base64_str = base64.b64encode(imageData.getvalue()).decode()
        print(type(base64_str), base64_str)
        random_uuid = uuid.uuid4()  # 生成一个随机数
        print(random_uuid)
        cache.set(random_uuid, data, timeout=300)  # 存到redis缓存中 有效期5分钟
        return JsonResponse({'code': 200, 'base64str': 'data:image/png;base64,' + base64_str, 'uuid': random_uuid})
<template>
  <div class="login">

    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
      <h3 class="title">python222 Django后台管理系统</h3>

      <el-form-item prop="username">

        <el-input
            v-model="loginForm.username"
            type="text"
            size="large"
            auto-complete="off"
            placeholder="账号"
        >
          <template #prefix><svg-icon icon="user" /></template>
        </el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
            v-model="loginForm.password"
            type="password"
            size="large"
            auto-complete="off"
            placeholder="密码"

        >
          <template #prefix><svg-icon icon="password" /></template>
        </el-input>
      </el-form-item>

      <el-form-item prop="code">
        <el-input
            v-model="loginForm.code"
            size="large"
            auto-complete="off"
            placeholder="验证码"
            style="width: 63%"
            @keyup.enter="handleLogin"
        >
          <template #prefix><svg-icon icon="validCode" /></template>
        </el-input>
        <div class="login-code">
          <img :src="codeUrl" @click="getCode" class="login-code-img"/>
        </div>
      </el-form-item>

      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
      <el-form-item style="width:100%;">
        <el-button
            size="large"
            type="primary"
            style="width:100%;"
            @click.prevent="handleLogin"
        >
          <span>登 录</span>

        </el-button>

      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2013-2022 <a href="http://www.python222.com" target="_blank">python222.com</a> 版权所有.</span>
    </div>
  </div>
</template>

<script setup>
  import { ref } from 'vue'
  import requestUtil from '@/utils/request'
  import { encrypt, decrypt } from "@/utils/jsencrypt"
  import qs from 'qs'
  import store from '@/store'
  import Cookies from "js-cookie"
  import router from "@/router";
  import {ElMessage} from 'element-plus'

  const codeUrl = ref("");

  const loginRef=ref(null);

  const loginForm = ref({
    username: "",
    password: "",
    rememberMe: false,
    code: "",
    uuid: ""
  });

  const loginRules = {
    username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
    password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
    code: [{ required: true, trigger: "change", message: "请输入验证码" }]
  };

  const getCode=async ()=>{

    let result=await requestUtil.get("/user/captcha");
    console.log(result)
    loginForm.value.uuid=result.data.uuid;
    codeUrl.value=result.data.base64str;
  }

  const handleLogin=()=>{
    loginRef.value.validate(async (valid)=>{
      if(valid){
        // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
        console.log("r"+loginForm.value.rememberMe)
        if (loginForm.value.rememberMe) {
          Cookies.set("username", loginForm.value.username, { expires: 30 });
          Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
          Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
        } else {
          // 否则移除
          Cookies.remove("username");
          Cookies.remove("password");
          Cookies.remove("rememberMe");
        }

        try{
          let result=await requestUtil.post("user/login?"+qs.stringify(loginForm.value));
          let data=result.data;
          if(data.code==200){
             // 生成token
             console.log(data.data)
             console.log('token:'+data.token)
             const currentUser=data.user;
             const token=data.token;
             const menuList=data.menuList;
             console.log("token:",token)
             store.commit('SET_TOKEN', token)
             console.log("currentUser:"+currentUser)
             currentUser.roles=data.roles
             store.commit("SET_USERINFO",currentUser);
             store.commit("SET_MENULIST",menuList);
             router.replace("/index")
          }else{
            ElMessage.error(data.info);
          }
        }catch(err){
          console.log("error:"+err);
          ElMessage.error("服务器出错,请联系管理员!");
        }
      }else{
        console.log("验证失败")
      }
    })
  }

  function getCookie() {
    const username = Cookies.get("username");
    const password = Cookies.get("password");
    const rememberMe = Cookies.get("rememberMe");
    loginForm.value = {
      username: username === undefined ? loginForm.value.username : username,
      password: password === undefined ? loginForm.value.password : decrypt(password),
      rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
    };
  }

  getCookie();
  getCode();

</script>

<style lang="scss" scoped>
a{
  color:white
}
.login {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}
.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}

.login-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;

  .el-input {
    height: 40px;



    input {
      display: inline-block;
      height: 40px;
    }
  }
  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 0px;
  }

}
.login-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}
.login-code {
  width: 33%;
  height: 40px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.el-login-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
.login-code-img {
  height: 40px;
  padding-left: 12px;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2122417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java超详细知识点——I/O流(字节流和字符流)

File类&#xff1a; Java API&#xff1a;java.io.File 类 是用来操作文件或文件夹的&#xff0c;无法用来读写 1.首先创建一下file的对象&#xff1a; 里面可以写相对路径或者绝对路径 File file new File("CCC.java"); 也可以使用其他构造方法 //String path …

【计算机网络】电路交换、报文交换和分组交换——三种交换方式性能分析以及计算机网络的分类

【计算机网络】电路交换、电报交换、分组交换 目录 【计算机网络】电路交换、电报交换、分组交换1. 电路交换2. 电报交换3. 分组交换4. 基于分组交换~“虚电路交换”技术 【计算机网络】电路交换、报文交换和分组交换——三种交换方式性能分析电路交换性能分析报文交换性能分析…

在AD域中恢复被删除的账户(LDP.EXE)

在域账户被不小心删除后&#xff0c;客户端使用域账号登陆会失败&#xff0c;账号不存在&#xff1b; 为了客户端登陆回原来账户文件下面&#xff0c;重新创建一个相同账户&#xff0c;域中此新账号的ID是新的&#xff0c;客户端登陆也会按新用户生成用户文件&#xff1b;同样复…

只用一个 HTML 元素可以写出多少形状?——动画篇

为期一个多月&#xff0c;我们针对只用一个 div 元素一共可以写出多少种形状的话题&#xff0c;通过六个篇章&#xff08;分了八篇文章&#xff09;进行了详细的展开。 其中&#xff0c;前三个篇章&#xff0c;我们主要围绕欧几里得几何中的基本形状做的展开&#xff0c;其中蕴…

基于Matlab和OpenCV的双目测距(标定和代码教程)

基于Matlab和OpenCV的双目测距研究 *摘要*&#xff1a;双目测距的原理是利用左右两个摄像机拍摄同一物体形成的视差来确定物体距摄像机的距离。这其中需要通过标定得出的参数包括内参&#xff08;焦距fc, 主点Principal point, 径向畸变Radial Distortion, 切向畸变Tangential…

828华为云征文 | 使用Flexus云服务器X实例部署Kubernetes图形化管理平台

828华为云征文 | 使用Flexus云服务器X实例部署Kubernetes图形化管理平台 1. 基础部署环境说明2. 部署Kubernetes环境3. 部署Kubernetes Dashboard4. 创建登录账号token5. 访问Kubernetes Dashboard 1. 基础部署环境说明 Kubernetes作为当今最流行的容器编排平台&#xff0c;随着…

【Kubernetes】常见面试题汇总(七)

目录 20.简述 Kubernetes 创建一个 Pod 的主要流程&#xff1f; 21.简述 Kubernetes 中 Pod 的重启策略&#xff1f; 20.简述 Kubernetes 创建一个 Pod 的主要流程&#xff1f; Kubernetes 中创建一个 Pod 涉及多个组件之间联动&#xff0c;主要流程如下&#xff1a; &#…

如何让Google收录我的网站?

其实仅仅只是收录&#xff0c;只要在GSC提交网址&#xff0c;等个两三天&#xff0c;一般就能收录&#xff0c;但收录是否会掉&#xff0c;这篇内容收录了是否有展现&#xff0c;排名&#xff0c;就是另外一个课题了&#xff0c;如果不收录&#xff0c;除了说明你的网站有问题&…

人工智能赋能千行百业

人工智能将赋能千行百业 人工智能&#xff08;AI&#xff09;作为当今科技领域的核心驱动力之一&#xff0c;正以前所未有的速度和广度赋能千行百业&#xff0c;深刻改变着我们的生产、生活方式以及社会经济的运行模式。以下是人工智能在多个行业中的赋能作用的几个关键方面&am…

QT多个界面

主函数 #include "widget.h" #include "second.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;Second s;QObject::connect(&w,&Widget::my_signals,&s,&Second::my_slots);w.…

全面质量管理知识竞赛题库

全面质量管理知识竞赛题库 第 1 章 质量 三、单项选择题 1.根据 ISO9000 标准的定义&#xff0c;“质量”是指“客体的一组固有特性满足要求的程度”&#xff0c;以下&#xff08; B &#xff09;不属于产 品的“固有特性”。 A.产品的寿命 B.产品的价格 C.产品制造和使用的…

民宿|基于java的民宿推荐系统(源码+数据库+文档)

民宿推荐系统 目录 基于java的民宿推荐系统 一、前言 二、系统设计 三、系统功能设计 系统功能实现 前台&#xff1a; 后台&#xff1a; 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌…

react-问卷星项目(1)

学习的一位MOOC老师的经验&#xff0c;记录一下学习的过程 本项目技术栈&#xff1a;React18TS4AntDesign5Next.js13 项目&#xff1a;低代码&#xff0c;B/C react官方文档 核心价值&#xff1a; 组件化&#xff1a;不是React原创&#xff0c;但在React中流行开来 数据驱…

怎么用python打开文件

python文件读写文件是最常见的IO操作。Python内置了读写文件的函数&#xff0c;用法和C是兼容的。 读写文件前&#xff0c;我们先必须了解一下&#xff0c;在磁盘上读写文件的功能都是由操作系统提供的&#xff0c;现代操作系统不允许普通的程序直接操作磁盘。 读写文件就是请…

使用java对栅格数据的处理,对栅格文件进行导入导出

需求背景&#xff1a; 对栅格文件进行导入导出&#xff08;使用代码的方式&#xff0c;非命令方式&#xff09;&#xff1b; 当然也可以使用代码和GDAL的方式进行&#xff0c;但是GDAL配置部署不便捷&#xff0c;故选用GeoTools方式来实现。 ps&#xff1a;若是使用命令方式&am…

大模型api谁家更便宜

1 openai 可点此链接查询价格&#xff1a;https://openai.com/api/pricing/ 2 百度 可点此链接查询价格&#xff1a;https://console.bce.baidu.com/qianfan/chargemanage/list 需要注意&#xff0c;百度千帆平台上还提供其他家的模型调用服务&#xff0c; 如llama, yi-34b等…

IDE快速复制文件名

在很多情况下我们需要复制IDE中文件的名称&#xff0c;习惯性的F2却不能重命名 如图操作又比较繁琐 解决方法⭐⭐⭐ 其实直接CtrlC可以复制文件名&#x1f921;&#x1f921;&#x1f921;

7-Zip压缩包如何添加密码,加密后如何取消

压缩包文件大家经常使用&#xff0c;最熟悉的肯定是RAR、ZIP格式压缩文件&#xff0c;但是7z压缩文件格式也很好用&#xff0c;它是三种压缩文件格式中压缩率最大的。想要将文件压缩到最小&#xff0c;使用7z格式可以达到最大化。那么在使用7z压缩格式的时候&#xff0c;我们可…

银河麒麟V10系统软件商店手动更新方法

银河麒麟桌面操作系统V10重新安装之后&#xff0c;有些软件商店未能及时的自动更新&#xff0c;从而软件商店里无法获取最新的软件应用&#xff0c;这个时候就需要我们手动的进行升级更新一下软件商店了&#xff0c;更新之后软件商店里的内容就会增加不少&#xff0c;那么&…

阿尔泰科技案例解析-炼钢厂设备监测解决方案!

案例背景 在一家大型钢铁冶炼厂中&#xff0c;熔炉是生产过程的核心设备&#xff0c;广泛应用于金属冶炼、铸造和加工步骤&#xff0c;如钢铁的冶炼和精炼。为了确保熔炉在最佳状态下运行&#xff0c;并防止潜在的故障或事故&#xff0c;该厂需要对熔炉及厂房内相关设备的多个…