我是使用sharedb 作為后端 ,然后前端使用的elementUI樣式,編寫的一個值班小工具。接下來,讓我們先來了解一下sharedb是什么吧?
- sharedb工具
github地址:https://github.com/share/sharedb.git。
官方概念:ShareDB是基於JSON文檔的操作轉換(OT)的實時數據庫后端。它是DerbyJS Web應用程序框架的實時后端。
特征:
-
- 實時同步任何JSON文檔
- 並發多用戶協作
- 具有異步最終一致性的同步編輯API
- 實時查詢訂閱
- 與任何數據庫輕松集成-MongoDB,PostgresQL(實驗性)
- 通過發布/訂閱集成可水平擴展
- 從文檔和操作中選擇所需字段的投影
- 用於實現訪問控制和自定義擴展的中間件
- 非常適合在瀏覽器或服務器上使用
- 重新連接后脫機更改同步
- 用於單元測試的數據庫和pub / sub的內存中實現
在這里,我只列出sharedb的概念以及特征,其中的其他特性(包括中間件等等),大家可以自行去github上去看。
對於我來說,我是把github上的代碼克隆下來,然后選擇了其中的counter作為例子模板,去編寫工具代碼,首先對其中的server.js進行更改.
在server.js中,我只改了 doc.create({ name1: [], date: [] }, callback);這一行代碼,在sharedb文檔中doc.create(data[, type][, options][, function(err) {...}])
在本地創建文檔,然后將創建操作發送到服務器。因為對於我的需求,就是要將名字,以及對應名字所選的日期發送到服務器上,所以在這里我創建了這個對象。
server.js
var http = require('http'); var express = require('express'); var ShareDB = require('sharedb'); var WebSocket = require('ws'); var WebSocketJSONStream = require('websocket-json-stream'); var backend = new ShareDB(); createDoc(startServer); // Create initial document then fire callback function createDoc(callback) { var connection = backend.connect(); var doc = connection.get('examples', 'counter'); doc.fetch(function (err) { if (err) throw err; if (doc.type === null) { doc.create({ name1: [], date: [] }, callback); return; }; callback(); }); } function startServer() { // Create a web server to serve files and listen to WebSocket connections var app = express(); app.use(express.static('static')); var server = http.createServer(app); // Connect any incoming WebSocket connection to ShareDB var wss = new WebSocket.Server({ server: server }); wss.on('connection', function (ws, req) { var stream = new WebSocketJSONStream(ws); backend.listen(stream); }); server.listen(8080); console.log('Listening on http://localhost:8080'); }
client.js代碼:
var sharedb = require('sharedb/lib/client'); // Open WebSocket connection to ShareDB server var socket = new WebSocket('ws://' + window.location.host); var connection = new sharedb.Connection(socket); // Create local Doc instance mapped to 'examples' collection document with id 'counter' var doc = connection.get('examples', 'counter'); // 獲取文檔的初始值並訂閱更改 doc.subscribe(showNames); doc.subscribe(dateRanges); // 當文檔更改時(由此客戶端或任何其他客戶端或服務器), // 更新頁面上的名字以及日期范圍 doc.on('op', showNames); doc.on('op', dateRanges);
function dateRanges() { app.$data.items = doc.data.date[0]; }; function showNames() { app.$data.messages1 = doc.data.name1[0] ? doc.data.name1[0] : []; }; // 當點擊提交名字的時候,更改本地的名字,並將同步更改服務器和其它鏈接的客戶端//提交日期 function submite() { doc.submitOp([{ p: ['date', 0], li: app.$data.items }]); }; //提交名字 function display() { doc.submitOp([{ p: ['name1', 0], li: app.$data.messages1 }]); }; // Expose to index.html global.display = display; global.submite = submite;
html代碼
這個代碼就沒什么好說的啦,主要功能 就是可以先輸入名字,然后再選擇想要進行值班的日期。而輸入的名字不可以有重復的,主要是靠一個復選框實現的,以及日期選擇器,感興趣的同學可以看看哦!,大家也可以看到這里面引用的js是bundle.js,所以我們要把寫在client.js中的代碼,復制到bundle.js中才可以實現哦。
<!DOCTYPE html> <meta charset="utf-8"> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <link rel="icon" href="1.ico"> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <title>值班小工具</title> <body> <div id="app" > <router-view> </router-view> </div> </body> <script src="dist/bundle.js"></script> <script> const inputName = { template: `<div> <el-header> <el-input id="date" placeholder="請輸入用戶名" name="date" v-model="message"></el-input> <el-button v-on:click="display1" type="primary">提交 名字</el-button> </el-header> <el-main> <span class="date">日 期</span> <span class="name">姓 名</span> <el-checkbox-group v-model="checked" > <li v-for="(item,index) in $root.items" :key="item.checked"> <el-checkbox v-on:change="checked1(index,checked)" :label="item" name="item" > {{item}} <span class="messages">{{[$root.messages1[index]].join("|")}}</span> </el-checkbox> </li> </el-checkbox-group> </el-main> </div>` , data() { name: inputName; return { message: '', messages1: [], checked: [] } }, methods: { checked1: function () { }, display1: function (index, checked) { var a = document.getElementsByName('item'); this.messages1 = this.$root.messages1; var cashName = document.getElementById("date").value; var result = cashName.match(/[\s`~!@#¥$%^&*()_+<>?:"{},.\/;'[\]\w]/im); for (var i = 0; i < a.length; i++) { if (result !== null) { this.$message({ message: '請輸入正確的名字哦!', type: 'warning' }); break } if (a[i].checked && this.messages1[i] == null) { this.messages1[i] = new Array; } if (a[i].checked && this.messages1[i] !== null && this.messages1[i][0] !== this.message) { this.messages1[i].push(this.message); } if (a[i].checked && this.messages1[i] !== null && this.messages1[i].length > 2) { this.messages1[i].pop(this.message); this.$message({ message: '前面已經有兩人選擇了哦,請選擇其它日期吧!', type: 'warning' }); } } display() } } }; const inputDate = { template: `<div> <el-header> <el-date-picker v-model="value" type="daterange" range-separator="至" start-placeholder="開始日期" end-placeholder="結束日期" format="yyyy年M月d日" value-format="yyyy-MM-dd" v-on:change="change1" > </el-date-picker> <el-button onclick="submite()" type="primary">提交 日期</el-button> </el-header> <el-main> <span class="date">日 期</span> <span class="name">姓 名</span> <li v-for="(item,index) in $root.items" :key="item.index"> {{item}} <span class="messages1">{{[$root.messages1[index]].join('&')}}</span> </li> </el-main> </div>`, data() { name: inputDate; return { items: [], value: '', } }, methods: { change1(value) { var begin = value[0]; var end = value[1]; var item = new Array(); app.$data.items = item; var ab = begin.split("-"); var ae = end.split("-"); var db = new Date(); db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]); var de = new Date(); de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]); var unixDb = db.getTime(); var unixDe = de.getTime(); for (var k = unixDb; k <= unixDe;) { item.push(new Date(parseInt(k)).format().toString()); k = k + 24 * 60 * 60 * 1000; } } } }; const routes = [ { path: '/inputDate', component: inputDate }, { path: '/inputName', component: inputName } ]; const router = new VueRouter({ routes }); Date.prototype.format = function () { var s = ""; s += this.getFullYear() + "-"; s += this.getMonth() + 1 + "-"; s += this.getDate(); return s; }; var app = new Vue({ router, data() { return { value6: '', items: [], checked: [], message: '', messages1: [], checked: [] } } }).$mount('#app'); </script> <style scoped> #app { /* width: 1200px; height: 848px; */ background-position: center; padding-top: 0%; } body { margin: 0%; } .el-checkbox__label { display: inline-block; padding-left: 10px; line-height: 19px; font-size: 14px; width: 100%; } .el-date-picker { width: 30%; } h3 { margin: 40px 0 0; } a { color: #42b983; } .el-header { color: #333; text-align: center; line-height: 88px; } li { padding-top: 2%; list-style-image: url('./4.ico'); } p { font-size: 20px; font-family: 'Segoe UI', Tahoma, Verdana, sans-serif } .date { padding-left: 10%; } .name { padding-left: 40%; } .el-main { padding-top: 1%; padding-left: 35%; width: 70%; height: 550px; } .el-input { width: 30%; } .messages1 { float: right; padding-right: 27%; text-decoration: underline; } .messages { float: right; padding-right: 24%; text-decoration: underline; } .el-checkbox { width: 80%; color: black; } </style>
第一次寫博客,可能有些地方寫的不是很完整,歡迎大家提出意見!