《程序猿入职必会(4) · Vue 完成 CURD 案例 》

news2024/9/9 1:00:43

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • 教师信息管理 CURD
      • 前文回顾
      • 日期格式化
      • 前端代码生成
      • 代码修修补补
    • 前端知识拓展
      • 图形化方式创建 Vue
      • 整合路由 Vue-Router
    • 总结陈词

CSDN.gif

写在前面的话

本系列博文已连载到第三回,通过前三回博文,我们已完成了前后端基础服务的搭建,也完成后端的完整接口清单,前端还差一些,本篇博文,把前端功能补充完整,让它像那么回事。
本篇博文,还有几个目标要完成:

  • 回顾一下之前三篇文章,将一些知识点补充一下;
  • 针对需求内容,把前端服务的CURD功能丰富起来;
  • 对部分前端细节不到位的地方进行修复,同时分享相关经验;

加油,程序猿,保持住Tempo,开干,玩的就是真实!

关联文章:
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》
《程序猿入职必会(2) · 搭建具备前端展示效果的 Vue》
《程序猿入职必会(3) · SpringBoot 各层功能完善 》
《程序猿入职必会(4) · Vue 完成 CURD 案例 》
《程序猿入职必会(5) · CURD 页面细节规范 》
《程序猿入职必会(6) · 返回结果统一封装》


教师信息管理 CURD

Tips:一直说CURD、CURD的,行业内的默认术语,如果有小伙伴不知道这是什么,那大概是代表创建(Create)、更新(Update)、读取(Read)和删除(Delete)操作的集合,可以指代某个实体表对应的维护页面,也是程序猿最基础的拧螺丝工作。

前文回顾

先回顾一下之前第二篇的结尾,我们引入 ElementUI 后,实现的效果如下。
效果还可以,不过只是功能的冰山一角。
image.png
不对,登记时间怎么是这个鬼样子?先给它修正,不然很别扭。

日期格式化

实现日期格式化可以后端来,也可以前端。

后端实现效果
Step1、引入 fastjson 依赖

<fastjson.version>2.0.33</fastjson.version>

<!-- JSON处理 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${fastjson.version}</version>
</dependency>

Step2、实体类添加注释

@JSONField(format = "yyyy-MM-dd HH:mm")
private java.util.Date createdTime;

Step3、前端正常展示

<span>{{ scope.row.createdTime}}</span>

效果如下:
image.png

前端实现效果
使用vue的管道符实现,代码如下:

<span>{{ scope.row.createdTime | timeFilter}}</span>

timeFilter(time) {
  // 这边将字符串进行日期转换
}

前端代码生成

这里先使用代码生成器生成教师实体对应的前端基础页面和接口,不然一个个写是挺耗时的,也比较基础。
企业实际开发中,这部分代码通常也是代码生成或者CV其他代码,再进行调整。
先来一段完整的Vue:

<template>
  <div class="app-container">

    <!-- 表头 查询与新增 -->
    <el-row>
      <el-col :span="24">
        <div class="filter-container">
          <el-input placeholder="关键词过滤" v-model="listQuery.query" style="width: 200px;" class="filter-item"
                    @keyup.enter.native="handleFilter"
          />
          <el-select v-model="listQuery.validFlag" placeholder="有效标志" clearable class="filter-item" style="width: 150px"
                     @change="handleFilter"
          >
            <el-option v-for="(value, key) in statusOptions" :key="key" :label="value" :value="key"/>
          </el-select>
          <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">搜索
          </el-button>
          <el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-circle-plus-outline"
                     @click="handleCreate"
          >新增
          </el-button>
        </div>
      </el-col>
    </el-row>

    <!-- 表格list -->
    <el-row>
      <el-col :span="24" :gutter="24">
        <el-table
          :row-class-name="rowClassName"
          v-loading="listLoading"
          :key="tableKey"
          :data="list"
          element-loading-text="Loading"
          border
          fit
          :height="tableHeight"
          style="width: 100%;"
          highlight-current-row
        >
          <el-table-column align="center" label="序号" width="80">
            <template slot-scope="scope">
              {{ scope.$index }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="教师编号" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.teaCode }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="教师名称" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.teaName }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="教师头像" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.teaImg }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="教师电话" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.teaPhone }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="教师学科" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.stuItem }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="教师身份" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.teaType }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="教师配置" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.teaConfig }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip label="排序号" show-overflow-tooltip min-width="10%" align="center">
            <template slot-scope="scope">
              {{ scope.row.sortNo }}
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip class-name="status-col" label="创建时间" min-width="20%" align="center">
            <template slot-scope="scope">
              <i class="el-icon-time"/>
              <span>{{ scope.row.createdTime }}</span>
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip class-name="status-col" label="修改时间" min-width="20%" align="center">
            <template slot-scope="scope">
              <i class="el-icon-time"/>
              <span>{{ scope.row.modifiedTime }}</span>
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip class-name="status-col" label="有效标志" min-width="10%" align="center">
            <template slot-scope="scope">
              <el-tag :type="scope.row.validFlag | statusFilter">{{ scope.row.validFlag | validFilter }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
            <template slot-scope="scope">
              <el-button size="mini" type="primary" @click="handleUpdate(scope.row)">
                编辑
              </el-button>
              <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-col>
    </el-row>

    <!-- 分页控件 -->
    <pagination v-show="total>0" :total="total"
                :page.sync="listQuery.pageNum"
                :limit.sync="listQuery.pageSize"
                layout="total, sizes, prev, pager, next"
                style="float:right;"
                @pagination="fetchData"
    />

    <!-- 编辑弹窗 -->
    <el-dialog
      :close-on-click-modal="false"
      :title="textMap[dialogStatus]"
      :visible.sync="dialogFormVisible"
    >
      <el-form
        ref="dataForm"
        :rules="rules"
        :model="temp"
        label-position="right"
        label-width="100px"
        style=""
      >
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="教师编号" label-width="105px" prop="teaCode">
              <el-input v-model="temp.teaCode" :disabled="dialogStatus === 'update'"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="教师名称" label-width="105px" prop="teaName">
              <el-input v-model="temp.teaName"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="教师头像" label-width="105px" prop="teaImg">
              <el-input v-model="temp.teaImg"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="教师电话" label-width="105px" prop="teaPhone">
              <el-input v-model="temp.teaPhone"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="教师学科" label-width="105px" prop="stuItem">
              <el-input v-model="temp.stuItem"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="教师身份" label-width="105px" prop="teaType">
              <el-input v-model="temp.teaType"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="教师配置" label-width="105px" prop="teaConfig">
              <el-input v-model="temp.teaConfig"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="排序号" label-width="105px" prop="sortNo">
              <el-input v-model.number="temp.sortNo"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
          </el-col>
          <el-col :span="12">
            <el-form-item label="有效标志" label-width="105px">
              <el-switch
                v-model="validSwitch"
                active-color="#13ce66"
                inactive-color="#ff4949"
              >
              </el-switch>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">确认</el-button>
        <el-button @click="dialogFormVisible = false">取消</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import ZyTeacherInfoApi from '@/api/study/ZyTeacherInfoApi'
import waves from '@/directive/waves' // Waves directive
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
export default {
  directives: { waves },
  components: { Pagination },
  filters: {
    statusFilter(status) {
      const statusMap = {
        1: 'success',
        2: 'blue',
        3: 'warning',
        4: 'info',
        0: 'danger'
      }
      return statusMap[status]
    },
    commonFilter(status, data) {
      if (status) {
        return data[status].text || data[status]
      } else {
        return ''
      }
    },
    validFilter(status) {
      const statusMap = {
        1: '有效',
        0: '作废'
      }
      return statusMap[status]
    },
    timeFilter(time) {
      if (time) {
        return new Date(time).Format('yyyy-MM-dd hh:mm:ss')
      } else {
        return ''
      }
    }
  },
  data() {
    return {
      tableKey: 0,//表格key值
      list: null, //表格对象
      listLoading: true, //表格加载框
      total: 0, //分页总数
      tableHeight: window.innerHeight - 220, //表格高度
      listQuery: { //表格查询对象
        pageNum: 1,
        pageSize: 10,
        query: '',
        validFlag: undefined,
        teaCode: ''
      },
      temp: {}, //编辑框临时变量
      statusOptions: { //有效无效下拉框
        '1': '有效',
        '0': '作废'
      },
      dialogFormVisible: false, //编辑框显示
      dialogStatus: '', //编辑框更新插入状态
      textMap: { //编辑框标题
        update: '编辑',
        create: '创建'
      },
      rules: { //编辑框校验规则
        teaCode: [{ required: true, message: '请输入教师编号', trigger: 'change' }],
        teaName: [{ required: true, message: '请输入教师名称', trigger: 'change' }],
        teaImg: [{ required: true, message: '请输入教师头像', trigger: 'change' }],
        teaPhone: [{ required: true, message: '请输入教师电话', trigger: 'change' }],
        stuItem: [{ required: true, message: '请输入教师学科', trigger: 'change' }],
        teaType: [{ required: true, message: '请输入教师身份', trigger: 'change' }],
        teaConfig: [{ required: true, message: '请输入教师配置', trigger: 'change' }],
        sortNo: [
          {
            type: 'number', required: false, message: '排序号必须为整数值', transform(value) {
              if (value === '' || value === null) {
                return 0
              }
              if (_.isInteger(value)) {
                return value
              } else {
                return false
              }
            }
          }
        ],
        validFlag: [{ required: true, message: '请输入有效标志', trigger: 'change' }]
      }
    }
  },
  computed: {
    validSwitch: {
      // getter
      get: function() {
        return this.temp.validFlag === '1'
      },
      // setter
      set: function(newValue) {
        if (newValue) {
          this.temp.validFlag = '1'
        } else {
          this.temp.validFlag = '0'
        }
      }
    }
  },
  created() {
    this.fetchData()
  },
  methods: {
    /**
     * 获取表格数据
     */
    fetchData() {
      let that = this
      this.listLoading = true
      this.$http.all([ZyTeacherInfoApi.getPage(this.listQuery)])
        .then(this.$http.spread(function(perms) {
          that.list = perms.rows
          that.total = perms.total
          that.listLoading = false
        }))
    },
    /**
     * 新增弹窗
     */
    handleCreate() {
      this.resetTemp()
      this.dialogStatus = 'create'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    /**
     * 清空弹窗内容
     */
    resetTemp() {
      this.temp = {
        teaCode: '',
        teaName: '',
        teaImg: '',
        teaPhone: '',
        stuItem: '',
        teaType: '',
        teaConfig: '',
        sortNo: '',
        createdTime: '',
        modifiedTime: '',
        validFlag: '1'
      }
    },
    /**
     * 确定新增
     */
    createData() {
      let that = this
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          ZyTeacherInfoApi.insert(this.temp).then(() => {
            this.dialogFormVisible = false
            this.$notify({
              title: '成功',
              message: '创建成功',
              type: 'success',
              duration: 1000,
              onClose() {
                that.fetchData()
              }
            })
          })
        }
      })
    },
    /**
     * 编辑弹窗
     */
    handleUpdate(row) {
      this.temp = Object.assign({}, row)
      this.dialogStatus = 'update'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    /**
     * 确认编辑
     */
    updateData() {
      let that = this
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          const tempData = Object.assign({}, this.temp)
          ZyTeacherInfoApi.update(tempData).then(() => {
            this.dialogFormVisible = false
            this.$notify({
              title: '成功',
              message: '更新成功',
              type: 'success',
              duration: 1000,
              onClose() {
                that.fetchData()
              }
            })
          })
        }
      })
    },
    /**
     * 删除操作
     */
    handleDelete(row) {
      let that = this
      ZyTeacherInfoApi.remove({ teaCode: row.teaCode }).then(() => {
        this.$notify({
          title: '成功',
          message: '删除成功',
          type: 'success',
          duration: 1000,
          onClose() {
            that.fetchData()
          }
        })
      })
    },
    /**
     * 搜索过滤
     */
    handleFilter() {
      this.listQuery.pageNum = 1
      this.fetchData()
    },

    rowClassName({ rowIndex }) {
      return rowIndex % 2 === 0 ? 'warning-row' : 'success-row'
    }
  }
}
</script>

<style>
.el-table .warning-row {
  background-color: #fff7e6;
}

.el-table .success-row {
  background-color: #f0f9eb;
}
</style>

再来一段完整的api:

import request from '@/utils/request'

export default {
  getList(params) {
    return request({
      url: '/zyTeacherInfo/', method: 'get', params
    })
  },

  get(params) {
    return request({
      url: '/zyTeacherInfo/' + params.teaCode, method: 'get', params
    })
  },

  getPage(params) {
    return request({
      url: '/zyTeacherInfo/page', method: 'get', params
    })
  },

  update(params) {
    return request({
      url: '/zyTeacherInfo/update', method: 'post', params
    })
  },

  insert(params) {
    return request({
      url: '/zyTeacherInfo/insert', method: 'post', params
    })
  },

  remove(params) {
    return request({
      url: '/zyTeacherInfo/delete', method: 'post', params
    })
  }
}

接着配一下路由,router.js

Tips:路由前面可能还没介绍,后续专题介绍。

{
  path: '/tea', component: () => import('@/views/study/ZyTeacherInfoManage'), hidden: true
},

最后,看一下效果,有那么回事吧。
image.png
image.png
操作一下增删改查,基本功能都正常,你敢相信,使用一套合适的代码生成器,仅仅几分钟就可以实现一个CURD功能。

代码修修补补

虽然功能全部正常,但是要拿去交差还是要完善一些。

【查询页面调整】
首先,表格页面代码生成的是全部字段,先把不重要的字段去掉,效果如下:image.png
表单元素太紧凑了,加一点间距,上一篇文章刚介绍了《企业实战分享 · CodeGeeX 初体验》,给它一个机会,练练手,如下所示:
image.png
生成很快,试试,效果如下图:
image.png
啧啧,什么鬼,没理解我意图,直接用 span 拆开间距了,也可能我没说清楚。
那给ChatGpt一个机会:
image.png
效果如下图,好像还不错,就这样吧。
image.png

【分页查询功能】
代码生成器是没办法那么智能知道你想用哪些字段作为搜索条件,当然生成的时候,给字段添加一些额外标识就另说,但感觉这样效率更低,还不如生成之后再来调整。
这边设计代码生成器的时候,固定保留了一个模糊搜索框和有效标志下拉框,可以覆盖大部分场景的最小需要。
看一下对应前端代码:
1、前端定义listQuery对象代表查询入参,传递query和validFlag两个属性,点击搜索触发handleFilter逻辑。
2、触发的JS逻辑,其实就是利用基于axios封装的请求工具,异步请求相关接口获取分页数据。

Tips:关于前端请求后端的工具封装,后续专栏介绍。

<el-input placeholder="关键词过滤" v-model="listQuery.query" class="filter-item input-item"
          @keyup.enter.native="handleFilter"
/>
<el-select v-model="listQuery.validFlag" placeholder="有效标志" clearable class="filter-item select-item"
           @change="handleFilter">
  <el-option v-for="(value, key) in statusOptions" :key="key" :label="value" :value="key"/>
</el-select>
<el-button v-waves class="filter-item button-item" type="primary" icon="el-icon-search" @click="handleFilter">
  搜索
</el-button>
/**
 * 搜索过滤
 */
handleFilter() {
  this.listQuery.pageNum = 1
  this.fetchData()
},
  
/**
 * 获取表格数据
 */
fetchData() {
  let that = this
  this.listLoading = true
  this.$http.all([ZyTeacherInfoApi.getPage(this.listQuery)])
    .then(this.$http.spread(function(perms) {
      that.list = perms.rows
      that.total = perms.total
      that.listLoading = false
    }))
},

再看看后端代码:
1、这边是借助PageHelper插件,轻松实现分页效果;
2、第二段是XML里面的SQL写法,参考一下即可,根据query模糊搜索,根据validFlag是精确查询;

@Service
public class ZyTeacherInfoService extends CrudService<ZyTeacherInfo, ZyTeacherInfoMapper> {

    /**
     * 获取用户分页列表
     *
     * @param query    搜索关键词
     * @param pageInfo 分页实体
     * @param zyTeacherInfo 实体入参
     * @return 用户列表
     */
    public PageInfo<ZyTeacherInfo> findListPage(String query, PageInfo pageInfo, ZyTeacherInfo zyTeacherInfo) {
        PageHelper.startPage(pageInfo);
        List<ZyTeacherInfo> zyTeacherInfolist = this.dao.findListPage(query, zyTeacherInfo);
        return new PageInfo<>(zyTeacherInfolist);
    }
}

@Mapper
public interface ZyTeacherInfoMapper extends BaseMapper<ZyTeacherInfo> {

    /**
     * 分页获取教师信息表列表
     *
     * @param query 搜索关键词
     * @param zyTeacherInfo 查询实体
     * @return 用户列表
     */
    List<ZyTeacherInfo> findListPage(@Param("query") String query, @Param("model") ZyTeacherInfo zyTeacherInfo);
}


<!-- 分页查询教师信息表列表 -->
<select id="findListPage" resultType="zyTeacherInfo">
    select
    t.*
    from
    zy_teacher_info t
    where 1=1
    <if test="query != null and query != ''">
        AND (INSTR(t.TEA_NAME , #{query})>0 OR t.TEA_CODE = #{query})
    </if>
    <if test="model.validFlag != null and model.validFlag != ''">
        and t.VALID_FLAG = #{model.validFlag}
    </if>
</select>

【关于增删改】
这部分属于基操,前面示例都能看懂,这边以更新为例简单说明。
前端:
1、填写完表单信息,触发updateData函数,先针对表单信息做一个校验(参考ElementUI);
2、触发 ZyTeacherInfoApi 接口,该接口也是利用 Axios,触发 Post 请求,调用后端;

updateData() {
  let that = this
  this.$refs['dataForm'].validate((valid) => {
    if (valid) {
      const tempData = Object.assign({}, this.temp)
      ZyTeacherInfoApi.update(tempData).then(() => {
        this.dialogFormVisible = false
        this.$notify({
          title: '成功',
          message: '更新成功',
          type: 'success',
          duration: 1000,
          onClose() {
            that.fetchData()
          }
        })
      })
    }
  })
},

update(params) {
  return request({
    url: '/zyTeacherInfo/update', method: 'post', params
  })
},

后端:好像没什么特别的,就是正常的 MyBatis - Update,搞定!


前端知识拓展

图形化方式创建 Vue

在这篇博文《程序猿学会 Vue · 基础与实战篇》中,介绍创建Vue项目的多种方式,漏介绍了一种图形化。
命令行输入:vue ui
会自动打开一个图形化页面,如下图,按步骤傻瓜式操作即可。

Tips:好像也没有什么特别的,不详细介绍。
Tips:SpringBoot也有类似界面,总之就是越来越简单了,怎么傻瓜怎么来。

image.png


整合路由 Vue-Router

【安装使用】
Step1、安装路由
npm install vue-router

Step2、编写路由配置文件,/router/index.js

import {createRouter, createWebHashHistory} from 'vue-router'
import HomeView from '../views/Home.vue'

const routes = [{
    path: '/home', name: 'home', component: HomeView
}, {
    path: '/about', name: 'about', component: () => import('../views/About.vue')
}]

const router = createRouter({
    history: createWebHashHistory(), routes
})

export default router

Step3、引入上述 JS,在 main.js 操作

// 引入路由
import router from './router/index.js'
app.use(router)

Step4、效果使用
App.vue 页面在合适地方添加:
地址栏输入/home或/about,内容会随之变化。


总结陈词

本系列博文更新到第四篇了,基本实现了教师的CURD功能。
接下来几篇博文将针对其中涉及的几个框架封装知识点进行专题说明。
总之,目标是帮助初入职场的大家,快速适应企业开发,多一些机会。
💗 如果觉得内容还可以,麻烦点个关注不迷路,您的鼓励是我创作的动力。

CSDN_END.gif

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

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

相关文章

【优选算法】——leetcode——438.找到字符串中所有字母异位词

目录 1.题目 2.题目理解 3.算法原理 1.如何快速判断两个字符串是否是异位词 2.解决问题 暴力求解——>滑动窗口哈希表 滑动窗口 利用滑动窗口哈希表解决问题 优化&#xff1a;更新结果的判断条件 4.编程代码 C代码 1.频率统计 2. 双指针 C语言代码 1.字符频率…

传统CS网络的新生——基于2G网络的远程灌溉实现

概述&#xff1a;iphone 实现远程电话触发&#xff0c;实现灌溉绿植的一般方法 方法一&#xff1a; 远程电话触发&#xff0c;音频线左右声道会产生一个信号&#xff0c;可以在后端利用SR锁存器暂存信号&#xff0c;后级可以接相应的控制电路实现灌溉。 方法二&#xff1a; 同…

记录阮一峰grid教程笔记

前言 看了阮一峰的grid教程&#xff0c;做一个笔记&#xff0c;主要自己看&#xff0c;有理解错误的地方后续更正&#xff0c;有新的理解后续补充。教程链接如下&#xff1a; CSS Grid 网格布局教程 - 阮一峰的网络日志 grid主要分为容器属性和项目的属性&#xff0c;在行列布…

React 学习——Context机制层级组件通信

核心思路&#xff1a;&#xff08;适用于所有层级&#xff0c;不仅仅是爷孙 父子&#xff09; createContext方法创建一个上下文对象在顶层组件 通过Provider组件提供数据在底层组件&#xff0c;通过useContext钩子函数使用数据 import { createContext, useContext } from …

NSSRound#4 Team

[NSSRound#4 SWPU]1zweb 考察&#xff1a;phar的反序列化 1.打开环境&#xff0c;审计代码 1.非预期解 直接用file伪协议读取flag,或直接读取flag file:///flag /flag 2.正常解法 用读取文件读取index.php,upload.php的源码 index.php: <?php class LoveNss{publi…

Java面试八股之Spring DAO的作用

Spring DAO的作用 Spring DAO (Data Access Object) 是 Spring 框架的一个重要组成部分&#xff0c;它提供了一套用于简化数据访问操作的抽象层。Spring DAO 的核心目的是使开发人员能够更容易地处理数据访问相关的异常&#xff0c;并提供一致的异常处理机制&#xff0c;同时简…

翻译: 可视化深度学习神经网络一

这是一个随意书写的28*28像素、分辨率很低的数字 3 但你的大脑一看见就能轻松辨识出来 &#xff0c;我想要你好好欣赏这点 人脑能够毫无障碍地辨识是非常厉害的 我的意思是&#xff0c;这个、这个、还有这个&#xff0c;都能被识别为 3 即使前后图像的图形组成有很大差异 当你…

什么情况?我代码没了

前两天检视代码时&#xff0c;发现PR里面有两个提交的描述信息一模一样&#xff0c;于是我提出应该将这两个提交合并成一个&#xff0c;保持提交树的清晰。 1 先储存起来&#xff01; 而同事这时正在开发别的特性&#xff0c;工作区不是干净的&#xff0c;没法直接执行 git r…

c程序杂谈系列(职责链模式与if_else)

从处理器的角度来说&#xff0c;条件分支会导致指令流水线的中断&#xff0c;所以控制语句需要严格保存状态&#xff0c;因为处理器是很难直接进行逻辑判断的&#xff0c;有可能它会执行一段时间&#xff0c;发现出错后再返回&#xff0c;也有可能通过延时等手段完成控制流的正…

python生成二维码指向说明书

python生成二维码转向文档 python生成二维码指向说明书 import qrcode# 生成包含本地文档路径的二维码 def generate_qrcode(local_file_path):qr qrcode.QRCode(version1,error_correctionqrcode.constants.ERROR_CORRECT_L,box_size10,border4,)qr.add_data(local_file_pa…

为什么要做边界值测试?

边界值测试的理解 边界值测试&#xff08;Boundary Value Testing&#xff09;是一种常用的软件测试方法&#xff0c;它侧重于测试输入值的边缘或临界条件。这些边缘条件通常包括最小值、最大值以及接近这些最小值和最大值的值。边界值测试的基本思想是&#xff0c;许多软件错…

微信支付API列表

接入前准备 更新时间&#xff1a;2023.08.24 在正式接入微信支付App服务前&#xff0c;你需要进行以下准备步骤&#xff1a; 选择接入模式&#xff1a;普通商户或普通服务商申请参数&#xff1a;AppID、商户号App支付页面规范 #选择接入模式 商户需要判断自己公司注册区域…

SuperMap GIS基础产品FAQ集锦(20240729)

一、SuperMap iDesktopX 问题1&#xff1a;您好&#xff0c;想请教一下&#xff0c;白模可以调整颜色吗 11.2.0 【解决办法】 右键白模图层&#xff0c;制作单值专题图&#xff0c;即可调整白模颜色。 问题2&#xff1a;这边有份矢量数据&#xff0c;导入到桌面里面要放很大…

Node.js + Axios 上传附件到 Gitee 仓库指定 Release

在软件开发过程中&#xff0c;自动化发布流程是提升效率的关键环节之一。本文将介绍如何使用 Node.js 和 Axios 库来自动化地向 Gitee 仓库的最新版本中上传发布包。通过读取项目中的 package.json 文件&#xff0c;获取版本信息&#xff0c;并自动将构建好的包文件上传到 Gite…

我们的前端开发逆天了!1 小时搞定了新网站,还跟我说 “不要钱”

大家好&#xff0c;我是程序员鱼皮。前段时间我们上线了一个新软件 剪切助手 &#xff0c;并且针对该项目做了一个官网&#xff1a; 很多同学表示官网很好看&#xff0c;还好奇是怎么做的&#xff0c;其实这个网站的背后还有个有趣的小故事。。。 鱼皮&#xff1a;我们要做个官…

【gofar远为门锁】酒店智能门锁源码 对接收银CyberWinApp-SAAS本地化-未来之窗行业应用跨平台架构

通过写房卡按钮写房卡 一、查看门锁读卡器信息 二、玄武星辰查到对应名称 如何知道自己家门锁的app&#xff0c;使用未来之窗【玄武芯辰】查询 通过上面我看出叫做gofar 在【玄武芯辰】输入gofar&#xff0c;人工智能会提示app信息 三、设置门锁控制app 在上一步找到app&a…

web服务器配置-(apache+nginx)

⼀、web基本概念和常识 Web&#xff1a;为⽤户提供的⼀种在互联⽹上浏览信息的服务&#xff0c;Web 服务是动态的、可交互的、跨平台的和图形化的。 Web 服务为⽤户提供各种互联⽹服务&#xff0c;这些服务包括信息浏览服务&#xff0c;以及各种交互式服务&#xff0c;包括聊…

LinuxCentos中安装apache网站服务详细教程

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

【Opencv】色彩空间 color space

import os import cv2 img cv2.imread(os.path.join(.,dog.jpg)) # 在opencv中使用imread,读取的图片每个像素都是bgr色彩&#xff0c;蓝色&#xff0c;绿色&#xff0c;红色 cv2.imshow(img,img) cv2.waitKey(0) # 颜色空间转化&#xff1a;BGR2RGB img_rgb cv2.cvtC…

爱快路由的dns强制客户端代理真是个强大的功能

大致情况是这样的&#xff1a;同事说在linux服务器/etc/resolv.conf上随便写个IP地址【不在线的】&#xff0c;dns地址也能解析&#xff0c;让我帮忙查查。 我看了下也感觉纳闷&#xff0c;试了下不光在服务器上&#xff0c;我本地的pc随便设置了个dns解析也是一样的。 通过wir…