最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基于 websocket 实现一个简易的信息填报工具

    正文概述 掘金(pengpeng)   2021-02-21   681

    起因

    因为最近打算离职了,看到各位大佬说要在简历中加上亮点,要着眼于平时的痛点,将其解决,然后将其陈述在简历上。
    然后我想着因为疫情的缘故,各种各样的填表简直让人防不胜防,填的心累,
    所以我就想着要是能搞个在线协同的小工具,直接在线填报,然后大家都填完之后导出Excel表就好了,说干就干

    需求分析

    1. 在线填报,实时更新,方便督促还有谁没有提交
    2. 所有人提交完成后,支持导出Excel

    效果图

    协同填报 基于 websocket 实现一个简易的信息填报工具 填报完后的导出 基于 websocket 实现一个简易的信息填报工具

    脚手架搭建

    前端就直接用 vue + elementui 的表格,直接一个html文件即可
    后端就用最简易的 nodejs + nodejs-websocket

    项目分析

    基于 websocket 实现一个简易的信息填报工具

    主要有三个文件

    • app.js 是后端主要代码
    • groupInfo.js 存放着项目组中每个成员的信息
    • index.html 是要分发给项目组中的每个成员的

    后端编写

    编写所需字段

    因为是模拟,所以就简单举几个字段
    uuid 员工的工号
    name 员工的姓名
    temp 当天体温

    前置知识

    websocket 以及 nodejs-websocket模块

    开始开发

    代码逻辑如下 团队成员的工号及姓名

    // 组员的工号及姓名
    module.exports = [
        { uuid: "111", name: "赵" },
        { uuid: "222", name: "钱" },
        { uuid: "333", name: "孙" },
        { uuid: "444", name: "李" },
        { uuid: "555", name: "周" },
        { uuid: "666", name: "吴" }
    ]
    

    具体业务代码如下 其中all_scoket是为了保留每个链接

    const ws = require("nodejs-websocket");
    
    const groupInfo = require("./groupInfo");
    
    const all_scoket = {}; // 保留每个用户的 id,最好是以 工号+当前时间 为 key,因为每个用户在同一时间只会有一次操作
    const server = ws
      .createServer(function (scoket) {
        scoket.on("text", function (str) {
          let data = JSON.parse(str);
          console.log(str);
          if (all_scoket[data.uuid]) {
            for (const iterator of groupInfo) {
              if (iterator.uuid === data.uuid.split("-")[0]) {
                iterator.temp=data.temp
                break;
              }
            }
            for (let item in all_scoket) {
              all_scoket[item].sendText(
                JSON.stringify(groupInfo)
              );
            }
          } else {
            for (const iterator of groupInfo) {
              if (iterator.uuid === data.uuid.split("-")[0]) {
                scoket.sendText(JSON.stringify(groupInfo));
                all_scoket[data.uuid] = scoket;
                return;
              }
            }
            console.log("用户不存在");
            scoket.sendText(
              JSON.stringify({
                error: "用户不存在",
              })
            );
    
          }
        });
        scoket.on("close", function (code, reason) {
          console.log("关闭连接", reason);
        });
        scoket.on("error", function (code, reason) {
          console.log("异常关闭", reason);
        });
      })
      .listen(3000, () => {
        console.log("running");
      });
    
    

    最后是前端代码

    注意:我用的是本地模拟方便展示,大家在尝试的时候换成内网ip就好了

    <html>
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1.0" />
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
        <link
          rel="stylesheet"
          href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
        />
        <script src="https://unpkg.com/element-ui/lib/index.js"></script>
        <script
          src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.core.min.js"
          integrity="sha512-qcjxCpal2fC5XTlJBB6yc/T2g7Xuxd0uHz+syZZEByojMPnKXroczpN3vrxL3ifHx4RVy4Jj8jVkXseQ5irtWA=="
          crossorigin="anonymous"
        ></script>
        <style>
          .user-info {
            margin-top: 200px;
            text-align: center;
          }
          .user-info .el-input {
            width: auto;
          }
        </style>
      </head>
    
      <body>
        <div id="app">
          <div>
            <div v-if="startInput" class="user-info">
              <el-input v-model="input" placeholder="请输入工号"></el-input>
              <el-button type="text" v-show="userName">{{userName}}</el-button>
              <el-button @click="getUserInfor">确认</el-button>
            </div>
            <div v-else>
              <el-table :data="tableData" stripe style="width: 100%">
                <el-table-column prop="uuid" label="工号">
                  <template slot-scope="scope">
                    <el-input
                      v-model="scope.row.uuid "
                      placeholder="请输入内容"
                      :disabled="true"
                    ></el-input>
                  </template>
                </el-table-column>
                <el-table-column prop="name" label="姓名" width="180">
                  <template slot-scope="scope">
                    <el-input
                      v-model="scope.row.name "
                      placeholder="请输入内容"
                      :disabled="true"
                    ></el-input>
                  </template>
                </el-table-column>
                <el-table-column prop="temp" label="体温" width="180">
                  <template slot-scope="scope">
                    <el-input
                      v-model="scope.row.temp"
                      placeholder="请输入体温"
                      :disabled="userId!==scope.row.uuid"
                    ></el-input>
                  </template>
                </el-table-column>
    
                <el-table-column label="操作">
                  <template slot-scope="scope">
                    <el-button
                      size="mini"
                      @click="submit(scope.$index, scope.row)"
                      v-if="userId===scope.row.uuid"
                      >提交</el-button
                    >
                  </template>
                </el-table-column>
              </el-table>
              <el-button @click="exportExcel">导出</el-button>
            </div>
          </div>
        </div>
        <script>
          
          let ws;
          function sendUuid(uuid) {
            ws.onopen = function () {
              console.log("连接成功");
              ws.send(JSON.stringify({ uuid }));
            };
          }
    
          function sendMessage({ name = "", temp = "", uuid = "" }) {
            ws.send(
              JSON.stringify({
                name,
                temp,
                uuid,
              })
            );
          }
    
          new Vue({
            el: "#app",
            data() {
              return {
                input: "",
                startInput: true,
                userName: "",
                userId: "",
                tableData: [],
              };
            },
            methods: {
              getUserInfor() {
              // 内网的话,将 127.0.0.1 改为自己的ip即可
                ws = new WebSocket("ws://127.0.0.1:3000");
                this.uuid = `${this.input}-${+new Date()}`;
                sendUuid(this.uuid);
                this.emitMessage(ws);
              },
              emitMessage(ws) {
                ws.onmessage = (data) => {
                  data = JSON.parse(data.data);
                  if (data.error) {
                    this.open();
                    return;
                  }
                  // 当后端返回正确的用户名时,则进入信息填报
                  this.userName = data[this.input];
                  this.userId = this.input;
                  this.tableData = data;
                  this.startInput = false;
                };
              },
              submit($index, $row) {
                console.log($index, $row);
                for (const key in $row) {
                  if (!$row[key]) {
                    return;
                  }
                }
                const row = Object.assign({}, $row);
                row.uuid = this.uuid;
                sendMessage(row);
              },
              exportExcel() {
                for (const iterator of this.tableData) {
                  if (!iterator.temp) {
                    return;
                  }
                }
                const excelData = [["工号", "姓名", "体温"]];
    
                for (let i = 0; i < this.tableData.length; i++) {
                  const element = this.tableData[i];
                  if (!Array.isArray(excelData[i + 1])) {
                    excelData[i + 1] = [];
                  }
                  excelData[i + 1].push(element.uuid, element.name, element.temp);
                }
                const sheet = XLSX.utils.aoa_to_sheet(excelData);
                openDownloadDialog(sheet2blob(sheet), "export.xlsx");
              },
              open() {
                this.$alert("请重新输入工号或联系 pengpeng ", "未查到此人", {
                  confirmButtonText: "确定",
                  callback: () => window.location.reload(),
                });
              },
            },
          });
          function openDownloadDialog(url, saveName) {
            if (typeof url == "object" && url instanceof Blob) {
              url = URL.createObjectURL(url); // 创建blob地址
            }
            var aLink = document.createElement("a");
            aLink.href = url;
            aLink.download = saveName || "";
            aLink.click();
          }
          function sheet2blob(sheet, sheetName) {
            sheetName = sheetName || "sheet1";
            var workbook = { SheetNames: [sheetName], Sheets: {} };
            workbook.Sheets[sheetName] = sheet; // 生成excel的配置项
            var wopts = {
              bookType: "xlsx", // 要生成的文件类型
              bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
              type: "binary",
            };
            var wbout = XLSX.write(workbook, wopts);
            var blob = new Blob([s2ab(wbout)], {
              type: "application/octet-stream",
            }); // 字符串转ArrayBuffer
            function s2ab(s) {
              var buf = new ArrayBuffer(s.length);
              var view = new Uint8Array(buf);
              for (var i = 0; i != s.length; ++i) {
                view[i] = s.charCodeAt(i) & 0xff;
              }
              return buf;
            }
            return blob;
          }
        </script>
      </body>
    </html>
    
    

    完整代码传送门


    起源地下载网 » 基于 websocket 实现一个简易的信息填报工具

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元