的工作學習筆記
計算機(computer)俗稱電腦,是現代一種用于高速計算的電子計算機器,可以進行數值計算,又可以進行邏輯計算,還具有存儲記憶功能。是能夠按照程序運行,自動、高速處理海量數據的現代化智能電子設備。
60天帶領小白學習計算機和編程,下面分享我的第二課
Excel:電子表格
功能:統計數據、制作圖表、數據分析
電子表格是現代辦公最常用的軟件沒有之一
如果office文件是97-2003版本,那么可以在之后所有的office版本中打開,但是如果office是2007之后的版本無法在97-2003版本中打開。office不支持向上兼容
保存97-2003版本:文件-另存為-選擇97-2003版本
輸入身份證號:需要將單元格數字屬性設置為文本
單元格坐標:由縱坐標和橫坐標表示,當需要表示一個區域時這個區域的斜對角線坐標可以表示這個區域例如:(C6:F11)
注:標點符號必須為英文標點符號
每一個工作表生成一個表格,不允許出現一張工作表中設置兩個表格的情況
創建表格:選擇需要創建表格的區域-右鍵-設置單元格格式-邊框標簽-選擇表格邊框的樣式外觀-單擊外邊框和內邊框
插入:右鍵插入,插入內容會出現在當前表格的左邊
數字遞增:選中當前表格-光標移動到右下角-按住ctrl-點擊向下拖拽
復制內容:選擇當前表格-光標移動到右下角-點擊向下拖拽
回車:光標向下移動
tab:光標向右移動
條件格式:
a、設置條件:設置大于-10分
b、根據設置條件設置突出顏色
表格樣式:在樣式選項中office給了默認選項,但是所有的表格需要嚴肅類型,顏色盡量使用冷色調
篩選排序:
a、選中需要排序的行或者列
b、選中篩選-點擊選中列或行的漏斗圖標
c、設置篩選條件或者排序條件-確定即可
背景色和前景色
a、背景色就是表格的底色,圖標為油桶
b、前景色就是字體顏色,圖標是大寫A
放大縮小表格:按ctrl+鼠標滾輪
凍結窗格:可以凍結首列或首行,方便在表格項太多時不清楚內容是什么
a、視圖標簽-凍結窗格-選擇目標單元格-凍結拆分窗格(以目標單元格為基準,向上和向左所有單元格全部凍結)
設置打印線:
選擇視圖-頁面布局-普通頁面-在表格中會出現虛線-這個虛線就是打印線
注:如果需要微調打印線位置可以通過頁面布局標簽-設置頁邊距
數據的有效性:用于設置選項
選擇數據-數據驗證-數據驗證-在允許中選擇序列-在來源中添加數據來源,數據來源1和數據來源2使用逗號隔開(必須是英文逗號)
求和:
=單元格坐標1+單元格左邊2
=F3+F5+F7
=sum(坐標1:坐標2)
=SUM(F3:F7)
=SUM(E4,F5,F8)代表求三個單元格相加(按住ctrl鼠標點擊)
平均值:average
=average(坐標1:坐標2)
=AVERAGE(E3:E7)
Execl:電子表格
引用:
a、表內引用:
在單元格內輸入=坐標,例如
=b3
b、表見引用:不同表格之間的引用
在單元格中輸入=表名稱!單元格坐標,例如
=表2!F5
(=手動鼠標切換表,點擊準備引用的單元格、回車)
遞增:
a、在單元格內輸入文本-鼠標點擊右下角-按住ctrl-向下拖拽
b、連續在兩個單元格內輸入文本-鼠標點擊右下角-向下拖拽
求和:
a、=坐標1+坐標2+坐標3
=C3+D3+E3
b、=sum(坐標1:坐標2)
=sum(C3:E3)
平均分:=average(坐標1:坐標2)
=average(D3:D10)
最大值最小值:
=max(坐標1:坐標2)
=max(D3:D10)
=min(坐標1:坐標2)
=min(D3:D10)
條件函數:
=if(條件,"結果1","結果2")
=IF(D3>=60,"及格","不及格")
注:if函數只有是或者否,沒有第三種情況,必須符號為英文輸入法
在生成圖表后-插入-圖表-選擇適當的圖表對應表格-調整圖表的樣式
隨機:=RANDBETWEEN(1,100)
between:在。。。。之間
在表格任意位置發生數據變化時就會重新產生一個隨機數
引用:=vlookup(引用值,引用數據范文,輸出列)
=VLOOKUP(B3,表3!A1:B18,2)
引用值:為B3
引用范圍是:表3的A1到B18之間的區域
輸出一個結果:輸出的結果為引用范圍的第2列
注意:引用范圍必須為成列的數據,輸出的結果必須是數字,每一個數字代表一列。
查看本機IP地址:
a、選擇桌面網絡快捷方式-右鍵-屬性-更改適配器設置-選擇網卡-右鍵-詳細信息-IPv4地址
b、windows+R-cmd回車-ipconfig-找到正在使用的網卡-查看ipv4地址
config
configure 配置
configuration
測試連通性:
ping+對端的IP地址,可以測試與對端的聯通是否正常
C:\Users\Administrator>ping 192.168.60.12
正在 Ping 192.168.60.12 具有 32 字節的數據:
來自 192.168.60.12 的回復: 字節=32 時間=1ms TTL=128
來自 192.168.60.12 的回復: 字節=32 時間=1ms TTL=128
來自 192.168.60.12 的回復: 字節=32 時間=1ms TTL=128
來自 192.168.60.12 的回復: 字節=32 時間=1ms TTL=128
TTL:生存時間,用于解決數據無線循環
Request timed out \請求超時
設置邊框:對角線設置,選擇單元格-右鍵-設置單元格-邊框-選擇樣式-點擊預覽圖周邊的對角線
國家數據:http://data.stats.gov.cn/
從國家數據導出數據-復制數據粘貼到本地建立的excel電子表格中-插入圖表-點擊畫筆可以修改圖表中的樣式(背景色和前景色必須形成反差)-點擊加號工具可以添加圖表的其他內容(例如各種網格和趨勢線)-點擊漏斗完成數據篩選
注:可以使用ctrl+鼠標的點擊拖拽完成數據的獨立選擇
鏡像文件:一種文件后綴為.iso的安裝包,需要使用虛擬光驅軟件運行才能使用
解決office激活:
a、安裝.NET ,要先于激活工具安裝
b、關閉防火墻安裝或者下載KMS軟件
1、對于win10操作系統,必須關閉windows defender防火墻-windows鍵-設置-更新和安全-windows安全-關閉windows defender防火墻
2、如果關閉windows defender防火墻沒有效果,那么需要下載其他防火墻覆蓋windows 防火墻權限
條形圖:當左側項目較多時,使用條形圖
餅圖:對單一項目進行詳細數據分析時使用餅圖
對總表中每一個數據添加單獨表項:選擇名字-右鍵-超鏈接-本文檔中位置-選擇對應的獨立表
save 保存 edit 編輯 user 用戶
如果在excel電子表格中出現超鏈接失敗,給出的提示時您的組織策略阻止。。。需要修改注冊表:
a、打開注冊表:windows+R-regedit
b、尋找路徑:HKEY_CURRENT_USER\Software\Classes\.html
c、修改值:雙擊.html-修改值為htmlfile
d、重啟計算機
設置excel電子表格密碼:
a、文件-另存為-設置保存路徑和文件名稱-工具-常規選項
b、添加只讀密碼和修改密碼
設置PC的用戶名密碼
我的電腦-右鍵-管理-本地用戶和組-選擇當前賬戶名稱-右鍵-設置密碼
數據透視圖:
a、選擇插入數據透視圖的表格頁面
b、插入-數據透視圖-同時選擇數據透視圖和數據透視表
c、勾選右邊篩選項(銷售員和總利潤)
注:在設置數據透視圖時,必須保證第一行各個表項名稱完整,不可以有空白表格
每年中國會發布互聯網報告:http://www.cnnic.net.cn
NIC:網卡
rule 規則
permit 允許
source 源、源頭
setup/install 安裝
1、兩口溫飽家庭收支系統
函數求和:=SUM(B4:M4)
數據透視圖:用于數據統計
在表格中-插入標簽-數據透視圖-數據透視圖和數據透視表-在新標簽中打開-勾選支出類型和匯總-根據數據透視表生成-收支表
求平均數函數=average(B4:M4)
Excel表格插件
安裝插件:
a、必須關閉所有office軟件
b、打開execl電子表格在視圖右邊出現2個新的標簽(選項卡,方方格子、DIY工具箱)
c、選擇身份-隨機身份證號-設置人數
在word中調用execl電子表格
a、負載excel電子表格-站提到word中,但是這種辦法不推薦(因為對表格的調整不方便)
b、在word中-插入-對象-由文件創建-瀏覽-找到excel電子表格-雙擊(進入在word中添加了excel表格功能)-修改表格
=randbetween(1,20):隨機生成一個1到20的隨機數
between:范圍
=VLOOKUP(B2,名單!A1:B20,2)
引用值 引用范圍 輸出第二列
網絡工程的最基本工作:完成通信
信號是什么?規則的電流、規則的電磁波
計算機發送信息依賴于二進制(0 1)->規則的電流就是以特定頻率波動電壓的電流
互聯網通信的幾大要素:終端、傳輸介質、IP地址、協議
a、終端設備:電腦、手機、pad、服務器等
b、傳輸介質:串口線、雙絞線(網線)、光纖、無線電
ADSL:下載速度遠大于上傳速度(10:1)
c、IP地址:IPv4、IPv6
d、協議:http 超文本傳輸協議
power point:PPT 幻燈片
point:點
幻燈片課件的內容必須為大綱,不可以為詳細說明書
新建幻燈片:在導航欄中鼠標右鍵-新建幻燈片-選擇建好的幻燈片-右鍵-版式
播放幻燈片:F5播放幻燈片,從當前頁播放:shift+F5
母版:制作統一樣式課件
a、打開母版:視圖-幻燈片母版-可以看到所有的幻燈片版式
b、分辨率問題:主流PC分辨率(1920*1080),將幻燈片設置為4:3-母版-幻燈片大小-4:3
c、隱藏背景:選擇幻燈片版式-勾選隱藏背景圖形,(新建背景:選擇背景-在背景對話框中-圖片和紋理填充-文件-添加背景圖片)
d、PPT目錄:插入-smart圖形-層次結構-創建圖形-添加形狀(添加新的文本框-升級-下移)-更改顏色和主題
e、標題降級和升級:選擇標題內容-tab(降級)-shift+tab(升級)
幻燈片切換動畫:切換-選擇細微性動畫效果
播放幻燈片畫筆:右鍵-指針選項-選擇畫筆樣式
獨立動畫效果:
a、進入:具體素材進入課件的動畫效果
b、強調:對素材重點顯示的動畫效果
c、退出:對素材退出當前課件頁面的動畫效果
d、路徑:設置素材自定義的移動路線
選擇素材-動畫-添加動畫-點擊動畫效果從上一項開始可以讓多個動畫的開始時間統一
導出演示文稿:在沒有office軟件時,課件依然可以使用
1、文件-導出-將演示文稿打包成CD(注:如果是演示文稿的PPT無法修改)
2、將文件后綴修改為ppt即可修改,不可以修改為pptx
注:ppt文件后綴為97-03版本office幻燈片文件后綴,pptx是2007之后版本幻燈片文件后綴
如何做一個好的PPT:因為現在很多公司將做PPT放入了入職要求中
a、布局:課件的邏輯必須清晰
b、圖片:所有添加的圖片必須像素足夠高(不可以使用手機照片)
c、字體:對非美術人員來說,所有字體全部使用微軟雅黑
d、圖標:如果會PS可以自己制作簡單矢量圖標,不會的話網站下載
ileUpload 對象
在網頁上傳文件,最核心元素就是這個HTML DOM的FileUpload對象了。什么鬼?好像不太熟啊~別急,看到真人就熟了:
<input type="file">
就是他啊!其實在 HTML 文檔中該標簽每出現一次,一個 FileUpload 對象就會被創建。該標簽包含一個按鈕,用來打開文件選擇對話框,以及一段文字顯示選中的文件名或提示沒有文件被選中。
把這個標簽放在<form>標簽內,設置form的action為服務器目標上傳地址,并點擊submit按鈕或通過JS調用form的submit()方法就可以實現最簡單的文件上傳了。
<form id="uploadForm" method="POST" action="upload" enctype="multipart/form-data">
<input type="file" id="myFile" name="file"></input>
<input type="submit" value="提交"></input>
</form>
這樣就完成功能啦?沒錯。但是你要是敢提交這樣的代碼,估計臉要被打腫
都什么年代了,我們要的是頁面無刷新上傳!
更優雅的上傳
現代網頁通過什么來實現用戶與服務器的無刷新交互?
——XMLHttpRequest
對,就是這個你很熟悉的家伙。如果你開發的產品支持的瀏覽器是現代瀏覽器,那么恭喜你,文件上傳就是這么easy!特別強調強調現代瀏覽器是因為我們接下來討論的XMLHttpRequest指的是XMLHttpRequest Level 2。
那什么是Level 1?為什么不行?因為它有如下限制:
而XMLHttpRequest Level 2針對這些缺陷做出了改進:
關于XMLHttpRequest的細節就不在這里贅述了,有興趣可以移步這篇博客。目前, 主流瀏覽器基本上都支持XHR2, 除了IE系列需要IE10及更高版本. 因此IE10以下是不支持XHR2的.
上面提到的FormData就是我們最常用的一種方式。通過在腳本里新建FormData對象,把File對象設置到表單項中,然后利用XMLHttpRequest異步上傳到服務器:
<form id="uploadForm" method="POST" action="upload" enctype="multipart/form-data">
<input type="file" id="myFile" name="file"></input>
<input type="submit" value="提交"></input>
</form>
完成最基本的需求無法滿足我們對用戶體驗的追求,所以我們還想要支持上傳進度顯示和上傳圖片預覽。
上傳進度
因為是XMLHttpRequest Level 2, 所以很容易就可以支持對上傳進度的監聽。細心地小伙伴會發現在chrome的developer tools的console里new一個XHR對象,調用點運算符就可以看到智能提示出來一個onprogress事件監聽器,那是不是我們只要綁定XHR對象的progress事件就可以了呢?
很接近了,但是XHR對象的直屬progress事件并不是用來監聽上傳資源的進度的。XHR對象還有一個屬性upload, 它返回一個XMLHttpRequestUpload 對象,這個對象擁有下列下列方法:
這些方法在XHR對象中都存在同名版本,區別是后者是用于加載資源時,而前者用于資源上傳時。其中onprogress 事件回調方法可用于跟蹤資源上傳的進度,它的event參數對象包含兩個重要的屬性loaded和total。分別代表當前已上傳的字節數(number of bytes)和文件的總字節數。比如我們可以這樣計算進度百分比:
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
var percentComplete = (event.loaded / event.total) * 100;
// 對進度進行處理 }
}
其中事件的lengthComputable屬性代表文件總大小是否可知。如果 lengthComputable 屬性的值是 false,那么意味著總字節數是未知并且 total 的值為零。
如果是現代瀏覽器,可以直接配合HTML5提供的
<progress id="myProgress" value="50" max="100">
</progress>
其value屬性綁定上面代碼中的percentComplete的值即可。再進一步我們還可以對<progress>的樣式統一調整,實現優雅降級方案,具體參見這篇文章。
再說說我在測試這個progress事件時遇到的一個問題。一開始我設在onprogress事件回調里的斷點總是只能走到一次,并且loaded值始終等于total。覺得有點詭異,改用console.log打印loaded值不見效,于是直接加大上傳文件的大小到50MB,終于看到了5個不同的百分比值。
因為xhr.upload.onprogress在上傳階段(即xhr.send()之后,xhr.readystate=2之前)觸發,每50ms觸發一次。所以文件太小網絡環境好的時候是直接到100%的。
圖片預覽
普通青年的圖片預覽方式是待文件上傳成功后,后臺返回上傳文件的url,然后把預覽圖片的img元素的src指向該url。這其實達不到預覽的效果和目的。
屬于文藝青年的現代瀏覽器又登場了:“使用HTML5的FileReader API吧!” 讓我們直接上代碼,直奔主題:
function handleImageFile(file) {
var previewArea = document.getElementById('previewArea');
var img = document.createElement('img');
var fileInput = document.getElementById("myFile");
var file = fileInput.files[0];
img.file = file;
previewArea.appendChild(img);
var reader = new FileReader();
reader.onload = (function(aImg) {
return function(e) {
aImg.src = e.target.result;
}
})(img);
reader.readAsDataURL(file);
}
這里我們使用FileReader來處理圖片的異步加載。在創建新的FileReader對象之后,我們建立了onload函數,然后調用readAsDataURL()開始在后臺進行讀取操作。當圖像文件加載后,轉換成一個 data: URL,并傳遞到onload回調函數中設置給img的src。
另外我們還可以通過使用對象URL來實現預覽:
var img = document.createElement("img");
img.src = window.URL.createObjectURL(file);;
img.onload = function() {
// 明確地通過調用釋放
window.URL.revokeObjectURL(this.src);
}
previewArea.appendChild(img);
多文件支持
什么?一個一個添加文件太煩?別急,打開一個開關就好了。別忘了我們文章一開頭就登場的FileUpload對象,它有一個multiple屬性。只要這樣
<input id="myFile" type="file" multiple>
我們就能在打開的文件選擇對話框中選中多個文件了。然后你在代碼里拿到的FileUpload對象的files屬性就是一個選中的多文件的數組了。
var fileInput = document.getElementById("myFile");
var files = fileInput.files;
var formData = new FormData();
for(var i = 0; i < files.length; i++) {
var file = files[i];
formData.append('files[]', file, file.name);
}
FormData的append方法提供第三個可選參數用于指定文件名,這樣就可以使用同一個表單項名,然后用文件名區分上傳的多個文件。這樣也方便前后臺的循環操作。
二進制上傳
有了FileReader,其實我們還有一種上傳的途徑,讀取文件內容后直接以二進制格式上傳。
var reader = new FileReader();
reader.onload = function(){
xhr.sendAsBinary(this.result);
}
// 把從input里讀取的文件內容,放到fileReader的result字段里
reader.readAsBinaryString(file);
不過chrome已經把XMLHttpRequest的sendAsBinary方法移除了。所以可能得自行實現一個
XMLHttpRequest.prototype.sendAsBinary = function(text){
var data = new ArrayBuffer(text.length);
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++){
ui8a[i] = (text.charCodeAt(i) & 0xff);
}
this.send(ui8a);
}
這段代碼將字符串轉成8位無符號整型,然后存放到一個8位無符號整型數組里面,再把整個數組發送出去。
到這里,我們應該可以結合業務需求實現一個比較優雅的文件上傳組件了。等等,哪里優雅了?都不支持拖拽!
拖拽的支持
利用HTML5的drag & drop事件,我們可以很快實現對拖拽的支持。首先我們可能需要確定一個允許拖放的區域,然后綁定相應的事件進行處理。看代碼
var dropArea;
dropArea = document.getElementById("dropArea");
dropArea.addEventListener("dragenter", handleDragenter, false);
dropArea.addEventListener("dragover", handleDragover, false);
dropArea.addEventListener("drop", handleDrop, false);
// 阻止dragenter和dragover的默認行為,這樣才能使drop事件被觸發function handleDragenter(e) {
e.stopPropagation();
e.preventDefault();
}
function handleDragover(e) {
e.stopPropagation();
e.preventDefault();
}
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
// handle files ...
}
這里可以把通過事件對象的dataTransfer拿到的files數組和之前相同處理,以實現預覽上傳等功能。有了這些事件回調,我們也可以在不同的事件給我們UI元素添加不同的class來實現更好交互效果。
好了,一個比較優雅的上傳組件可以進入生產模式了。什么?還要支持IE9?好吧,讓我們來看看IE10以下的瀏覽器如何實現無刷新上傳。
借用iframe
之前說了要實現文件上傳使用FileUpload對象()即可。這在低版本的IE里也是適用的。那我們為什么還要用iframe呢?
因為在現代瀏覽器中我們可以用XMLHttpRequest Level 2來支持二進制數據,異步文件上傳,并且動態創建FormData。而低版本的IE里的XMLHttpRequest是Level 1。所以我們通過XHR異步向服務器發上傳請求的路走不通了。只能老老實實的用form的submit。
而form的submit會導致頁面的刷新。原因分析好了,那么答案就近在咫尺了。我們能不能讓form的submit不刷新整個頁面呢?答案就是利用iframe。把form的target指定到一個看不見的iframe,那么返回的數據就會被這個iframe接受,于是乎就只有這個iframe會刷新。而它又是看不見的,用戶自然就感知不到了。
window.__iframeCount = 0;
var hiddenframe = document.createElement("iframe");
var frameName = "upload-iframe" + ++window.__iframeCount;
hiddenframe.name = frameName;
hiddenframe.id = frameName;
hiddenframe.setAttribute("style", "width:0;height:0;display:none");
document.body.appendChild(hiddenframe);
var form = document.getElementById("myForm");
form.target = frameName;
然后響應iframe的onload事件,獲取response
hiddenframe.onload = function(){
// 獲取iframe的內容,即服務返回的數據
var resData = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent;
// 處理數據 。。。
//刪除iframe setTimeout(function(){
var _frame = document.getElementById(frameName);
_frame.parentNode.removeChild(_frame);
}, 100);
}
iframe的實現大致如此,但是如果文件上傳的地址與當前頁面不在同一個域下就會出現跨域問題。導致iframe的onload回調里的訪問服務返回的數據失敗。
這時我們再祭出JSONP這把利劍,來解決跨域問題。首先在上傳之前注冊一個全局的函數,把函數名發給服務器。服務器需要配合在response里讓瀏覽器直接調用這個函數。
// 生成全局函數名,避免沖突var CALLBACK_NAME = 'CALLBACK_NAME';
var genCallbackName = (function () {
var i = 0;
return function () {
return CALLBACK_NAME + ++i;
};
})();
var curCallbackName = genCallbackName();
window[curCallbackName] = function(res) {
// 處理response 。。。
// 刪除iframe
var _frame = document.getElementById(frameName);
_frame.parentNode.removeChild(_frame);
// 刪除全局函數本身
window[curCallbackName] = undefined;
}
// 如果已有其他參數,這里需要判斷一下,改為拼接 &callback=
form.action = form.action + '?callback=' + curCallbackName;
好了,實現一個文件上傳組件的基本知識點大致總結了一下。
1.1技術方案
本項目使用MQ實現頁面發布的技術方案如下:
技術方案說明:
1、平臺包括多個站點,頁面歸屬不同的站點。
2、發布一個頁面應將該頁面發布到所屬站點的服務器上。
3、每個站點服務部署cms client程序,并與交換機綁定,綁定時指定站點Id為routingKey。指定站點id為routingKey就可以實現cms client只能接收到所屬站點的頁面發布消息。
4、頁面發布程序向MQ發布消息時指定頁面所屬站點Id為routingKey,將該頁面發布到它所在服務器上的cms client。
路由模式分析如下:
發布一個頁面,需發布到該頁面所屬的每個站點服務器,其它站點服務器不發布。
比如:發布一個門戶的頁面,需要發布到每個門戶服務器上,而用戶中心服務器則不需要發布。
所以本項目采用routing模式,用站點id作為routingKey,這樣就可以匹配頁面只發布到所屬的站點服務器上。
頁面發布流程圖如下:
1、前端請求cms執行頁面發布。
2、cms執行靜態化程序生成html文件。3、cms將html文件存儲到GridFS中。
4、cms向MQ發送頁面發布消息
5、MQ將頁面發布消息通知給Cms Client 6、Cms Client從GridFS中下載html文件
7、Cms Client將html保存到所在服務器指定目錄
1.2頁面發布消費方
1.2.1需求分析
功能分析:
創建Cms Client工程作為頁面發布消費方,將Cms Client部署在多個服務器上,它負責接收到頁面發布 的消息后從
GridFS中下載文件在本地保存。
需求如下:
1、將cms Client部署在服務器,配置隊列名稱和站點ID。2、cms Client連接RabbitMQ并監聽各自的“頁面發布隊列” 3、cms Client接收頁面發布隊列的消息
4、根據消息中的頁面id從mongodb數據庫下載頁面到本地
調用dao查詢頁面信息,獲取到頁面的物理路徑,調用dao查詢站點信息,得到站點的物理路徑 頁面物理路徑=站點物理路徑+頁面物理路徑+頁面名稱。
從GridFS查詢靜態文件內容,將靜態文件內容保存到頁面物理路徑下。
1.2.2創建Cms Client工程
1、創建maven工程pom.xml
<?xml version="1.0" encoding="UTF‐8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven‐4.0.0.xsd"> <parent> <artifactId>xc‐framework‐parent</artifactId> <groupId>com.xuecheng</groupId> <version>1.0‐SNAPSHOT</version> <relativePath>../xc‐framework‐parent/pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>xc‐service‐manage‐cms‐client</artifactId> <dependencies> <dependency> <groupId>com.xuecheng</groupId> <artifactId>xc‐framework‐model</artifactId> <version>1.0‐SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐data‐mongodb</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons‐io</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> </dependencies> </project>
2、配置文件
在resources下配置application.yml和logback-spring.xml。
application.yml的內容如下:
server: port: 31000 spring: application: name: xc‐service‐manage‐cms‐client data: mongodb: uri: mongodb://root:123@localhost:27017 database: xc_cms rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest virtualHost: / xuecheng: mq: #cms客戶端監控的隊列名稱(不同的客戶端監控的隊列不能重復) queue: queue_cms_postpage_01 routingKey: 5a751fab6abb5044e0d19ea1 #此routingKey為門戶站點ID
說明:在配置文件中配置隊列的名稱,每個 cms client在部署時注意隊列名稱不要重復3、啟動類
@SpringBootApplication @EntityScan("com.xuecheng.framework.domain.cms")//掃描實體類 @ComponentScan(basePackages={"com.xuecheng.framework"})//掃描common下的所有類 @ComponentScan(basePackages={"com.xuecheng.manage_cms_client"}) public class ManageCmsClientApplication { public static void main(String[] args) { SpringApplication.run(ManageCmsClientApplication.class, args); } }
1.2.3RabbitmqCon?g配置類
消息隊列設置如下:
1、創建“ex_cms_postpage”交換機
2、每個Cms Client創建一個隊列與交換機綁定
3、每個Cms Client程序配置隊列名稱和routingKey,將站點ID作為routingKey。
package com.xuecheng.manage_cms_client.config; import org.springframework.amqp.core.*; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** *@author Administrator *@version 1.0 **/ @Configuration public class RabbitmqConfig { //隊列bean的名稱 public static final String QUEUE_CMS_POSTPAGE = "queue_cms_postpage"; //交換機的名稱 public static final String EX_ROUTING_CMS_POSTPAGE="ex_routing_cms_postpage"; //隊列的名稱@Value("${xuecheng.mq.queue}") public String queue_cms_postpage_name; //routingKey 即 站 點 Id @Value("${xuecheng.mq.routingKey}") public String routingKey; /** *交換機配置使用direct類型 *@return the exchange */ @Bean(EX_ROUTING_CMS_POSTPAGE) public Exchange EXCHANGE_TOPICS_INFORM() { return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build(); } //聲明隊列@Bean(QUEUE_CMS_POSTPAGE) public Queue QUEUE_CMS_POSTPAGE() { Queue queue = new Queue(queue_cms_postpage_name); return queue; } /** *綁定隊列到交換機 * *@param queue the queue *@param exchange the exchange * @return the binding */ @Bean public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_CMS_POSTPAGE) Queue queue, @Qualifier(EX_ROUTING_CMS_POSTPAGE) Exchange exchange) { return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs(); } }
1.2.4定義消息格式
消息內容采用json格式存儲數據,如下: 頁面id:發布頁面的id
{ "pageId":"" }
1.2.5PageDao
1、使用CmsPageRepository 查詢頁面信息
public interface CmsPageRepository extends MongoRepository<CmsPage,String> { }
2、使用CmsSiteRepository查詢站點信息,主要獲取站點物理路徑
public interface CmsSiteRepository extends MongoRepository<CmsSite,String> { }
1.2.6PageService
在Service中定義保存頁面靜態文件到服務器物理路徑方法:
package com.xuecheng.manage_cms_client.service;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream; import com.mongodb.client.gridfs.model.GridFSFile; import com.xuecheng.framework.domain.cms.CmsPage; import com.xuecheng.framework.domain.cms.CmsSite;
import com.xuecheng.framework.domain.cms.response.CmsCode; import com.xuecheng.framework.exception.ExceptionCast; import com.xuecheng.manage_cms_client.dao.CmsPageRepository;
import com.xuecheng.manage_cms_client.dao.CmsSiteRepository;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.gridfs.GridFsResource; import org.springframework.data.mongodb.gridfs.GridFsTemplate; import org.springframework.stereotype.Service;
import java.io.*;
import java.util.Optional;
/**
*@author Administrator
*@version 1.0
**/ @Service
public class PageService {
@Autowired
CmsPageRepository cmsPageRepository; @Autowired
CmsSiteRepository cmsSiteRepository;
@Autowired
GridFsTemplate gridFsTemplate; @Autowired
GridFSBucket gridFSBucket;
//將頁面html保存到頁面物理路徑
public void savePageToServerPath(String pageId){
Optional<CmsPage> optional = cmsPageRepository.findById(pageId); if(!optional.isPresent()){
ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
}
//取出頁面物理路徑
CmsPage cmsPage = optional.get();
//頁面所屬站點
CmsSite cmsSite = this.getCmsSiteById(cmsPage.getSiteId());
//頁面物理路徑
String pagePath = cmsSite.getSitePhysicalPath() + cmsPage.getPagePhysicalPath() + cmsPage.getPageName();
//查詢頁面靜態文件
String htmlFileId = cmsPage.getHtmlFileId(); InputStream inputStream = this.getFileById(htmlFileId); if(inputStream == null){
ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_HTMLISNULL);
}
FileOutputStream fileOutputStream = null; try {
fileOutputStream = new FileOutputStream(new File(pagePath));
//將文件內容保存到服務物理路徑IOUtils.copy(inputStream,fileOutputStream);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) { e.printStackTrace();
}
try {
fileOutputStream.close();
} catch (IOException e) { e.printStackTrace();
}
}
}
//根據文件id獲取文件內容
public InputStream getFileById(String fileId){ try {
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream); return gridFsResource.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//根據站點id得到站點
public CmsSite getCmsSiteById(String siteId){
Optional<CmsSite> optional = cmsSiteRepository.findById(siteId); if(optional.isPresent()){
CmsSite cmsSite = optional.get(); return cmsSite;
}
return null;
}
}
1.2.6ConsumerPostPage
在cms client工程的mq包下創建ConsumerPostPage類,ConsumerPostPage作為發布頁面的消費客戶端,監聽頁面發布隊列的消息,收到消息后從mongodb下載文件,保存在本地。
package com.xuecheng.manage_cms_client.mq; import com.alibaba.fastjson.JSON; import com.xuecheng.framework.domain.cms.CmsPage; import com.xuecheng.manage_cms_client.dao.CmsPageRepository; import com.xuecheng.manage_cms_client.service.PageService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Map; import java.util.Optional; /** *@author Administrator *@version 1.0 **/ @Component public class ConsumerPostPage { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerPostPage.class); @Autowired CmsPageRepository cmsPageRepository; @Autowired PageService pageService; @RabbitListener(queues={"${xuecheng.mq.queue}"}) public void postPage(String msg){ //解析消息 Map map = JSON.parseObject(msg, Map.class); LOGGER.info("receive cms post page:{}",msg.toString()); //取出頁面id String pageId = (String) map.get("pageId"); //查詢頁面信息 Optional<CmsPage> optional = cmsPageRepository.findById(pageId); if(!optional.isPresent()){ LOGGER.error("receive cms post page,cmsPage is null:{}",msg.toString()); return ; } //將頁面保存到服務器物理路徑pageService.savePageToServerPath(pageId); } }
1.3頁面發布生產方
1.3.1需求分析
管理員通過 cms系統發布“頁面發布”的消費,cms系統作為頁面發布的生產方。需求如下:
1、管理員進入管理界面點擊“頁面發布”,前端請求cms頁面發布接口。
2、cms頁面發布接口執行頁面靜態化,并將靜態化頁面存儲至GridFS中。
3、靜態化成功后,向消息隊列發送頁面發布的消息。
1)獲取頁面的信息及頁面所屬站點ID。
2)設置消息內容為頁面ID。(采用json格式,方便日后擴展)
3)發送消息給ex_cms_postpage交換機,并將站點ID作為routingKey。
1.3.2RabbitMQ配置
1、配置Rabbitmq的連接參數
在application.yml添加如下配置:
spring: rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest virtualHost: /
2、在pom.xml添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐amqp</artifactId> </dependency>
3、RabbitMQCon?g配置
由于cms作為頁面發布方要面對很多不同站點的服務器,面對很多頁面發布隊列,所以這里不再配置隊列,只需要 配置交換機即可。
在cms工程只配置交換機名稱即可。
package com.xuecheng.manage_cms.config; import org.springframework.amqp.core.*; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; package com.xuecheng.manage_cms_client.config; import org.springframework.amqp.core.*; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitmqConfig { //交換機的名稱 public static final String EX_ROUTING_CMS_POSTPAGE="ex_routing_cms_postpage"; /** *交換機配置使用direct類型 *@return the exchange */ @Bean(EX_ROUTING_CMS_POSTPAGE) public Exchange EXCHANGE_TOPICS_INFORM() { return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build(); } }
1.3.3Api接口
在api工程定義頁面發布接口:
@ApiOperation("發布頁面") public ResponseResult post(String pageId);
1.3.4PageService
在PageService中定義頁面發布方法,代碼如下:
//頁面發布 public ResponseResult postPage(String pageId){ //執行靜態化 String pageHtml = this.getPageHtml(pageId); if(StringUtils.isEmpty(pageHtml)){ ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_HTMLISNULL); } //保存靜態化文件 CmsPage cmsPage = saveHtml(pageId, pageHtml); //發送消息sendPostPage(pageId); return new ResponseResult(CommonCode.SUCCESS); } //發送頁面發布消息 private void sendPostPage(String pageId){ CmsPage cmsPage = this.getById(pageId); if(cmsPage == null){ ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS); } Map<String,String> msgMap = new HashMap<>(); msgMap.put("pageId",pageId); //消息內容 String msg = JSON.toJSONString(msgMap); //獲取站點id作為routingKey String siteId = cmsPage.getSiteId(); //發布消息this.rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE,siteId, msg); } //保存靜態頁面內容 private CmsPage saveHtml(String pageId,String content){ //查詢頁面 Optional<CmsPage> optional = cmsPageRepository.findById(pageId); if(!optional.isPresent()){ ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS); } CmsPage cmsPage = optional.get(); //存儲之前先刪除 String htmlFileId = cmsPage.getHtmlFileId(); if(StringUtils.isNotEmpty(htmlFileId)){ gridFsTemplate.delete(Query.query(Criteria.where("_id").is(htmlFileId))); } //保存html文件到GridFS InputStream inputStream = IOUtils.toInputStream(content); ObjectId objectId = gridFsTemplate.store(inputStream, cmsPage.getPageName()); //文件id String fileId = objectId.toString(); //將文件id存儲到cmspage中cmsPage.setHtmlFileId(fileId); cmsPageRepository.save(cmsPage); return cmsPage; }
1.3.5CmsPageController
編寫Controller實現api接口,接收頁面請求,調用service執行頁面發布。
@Override @PostMapping("/postPage/{pageId}") public ResponseResult post(@PathVariable("pageId") String pageId) { return pageService.postPage(pageId); }
1.4頁面發布前端
用戶操作流程:
1、用戶進入cms頁面列表。
2、點擊“發布”請求服務端接口,發布頁面。
3、提示“發布成功”,或發布失敗。
1.4.1API方法
在 cms前端添加 api方法。
/*發布頁面*/ export const page_postPage= id => { return http.requestPost(apiUrl+'/cms/page/postPage/'+id) }
1.4.2頁面
修改page_list.vue,添加發布按鈕
<el‐table‐column label="發布" width="80"> <template slot‐scope="scope"> <el‐button size="small" type="primary" plain @click="postPage(scope.row.pageId)">發布 </el‐button> </template> </el‐table‐column>
添加頁面發布事件:
postPage (id) { this.$confirm('確認發布該頁面嗎?', '提示', { }).then(() => { cmsApi.page_postPage(id).then((res) => { if(res.success){ console.log(' 發 布 頁 面 id='+id); this.$message.success('發布成功,請稍后查看結果'); }else{ this.$message.error('發布失敗'); } }); }).catch(() => { }); },
1.5測試
這里測試輪播圖頁面修改、發布的流程:
1、修改輪播圖頁面模板或修改輪播圖地址
注意:先修改頁面原型,頁面原型調試正常后再修改頁面模板。
2、執行頁面預覽
3、執行頁面發布,查看頁面是否寫到網站目錄
4、刷新門戶首頁并觀察輪播圖是否變化。
1.6思考
1、如果發布到服務器的頁面內容不正確怎么辦?
2、一個頁面需要發布很多服務器,點擊“發布”后如何知道詳細的發布結果?
3、一個頁面發布到多個服務器,其中有一個服務器發布失敗時怎么辦?
2.1需求分析
在線教育平臺的課程信息相當于電商平臺的商品。課程管理是后臺管理功能中最重要的模塊。本項目為教學機構提 供課程管理功能,教學機構可以添加屬于自己的課程,供學生在線學習。
課程管理包括如下功能需求:
1、分類管理
2、新增課程
3、修改課程
4、預覽課程
5、發布課程
用戶的操作流程如下:
1、進入我的課程
2、點擊“添加課程”,進入添加課程界面
3、輸入課程基本信息,點擊提交
4、課程基本信息提交成功,自動進入“管理課程”界面,點擊“管理課程”也可以進入“管理課程”界面
5、編輯圖片
上傳課程圖片。
5、編輯圖片
上傳課程圖片。
7、編輯課程計劃
添加課程計劃:
2.2教學方法
本模塊對課程信息管理功能的教學方法采用實戰教學方法,旨在通過實戰提高接口編寫的能力,具體教學方法如 下:
1、前后端工程導入
教學管理前端工程采用與系統管理工程相同的技術,直接導入后在此基礎上開發。
課程管理服務端工程采用Spring Boot技術構建,技術層技術使用Spring data Jpa(與Spring data Mongodb類似)、Mybatis,直接導入后在此基礎上開發。
2、課程計劃功能
課程計劃功能采用全程教學。
3、我的課程、新增課程、修改課程、課程營銷
我的課程、新增課程、修改課程、課程營銷四個功能采用實戰方式,課堂上會講解每個功能的需求及技術點,講解 完成學生開始實戰,由導師進行技術指導。
4、參考文檔
實戰結束提供每個功能的開發文檔,學生參考文檔并修正功能缺陷。
2.3環境搭建
2.3.1搭建數據庫環境
1)創建數據庫
課程管理使用MySQL數據庫,創建課程管理數據庫:xc_course。導入xc_course.sql腳本
2)數據表介紹
課程信息內容繁多,將課程信息分類保存在如下表中:
數據表結構如下:
2.3.2導入課程管理服務工程
1)持久層技術介紹:
課程管理服務使用MySQL數據庫存儲課程信息,持久層技術如下:
1、spring data jpa:用于表的基本CRUD。2、mybatis:用于復雜的查詢操作。
3、druid:使用阿里巴巴提供的spring boot 整合druid包druid-spring-boot-starter管理連接池。
druid-spring-boot-starter地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
2)導入工程
導入資料下的“xc-service-manage-course.zip”。
2.3.3導入課程管理前端工程
課程管理屬于教學管理子系統的功能,使用用戶為教學機構的管理人員和老師,為保證系統的可維護性,單獨創建 一個教學管理前端工程。 教學管理前端工程與系統管理前端的工程結構一樣,也采用vue.js框架來實現。
從課程資料目錄拷貝xc-ui-pc-teach.zip到工程,使用webstorm打開,啟動工程: 效果圖如下:
3.1需求分析
什么是課程計劃?
課程計劃定義了課程的章節內容,學生通過課程計劃進行在線學習,下圖中右側顯示的就是課程計劃。
課程計劃包括兩級,第一級是課程的大章節、第二級是大章節下屬的小章節,每個小章節通常是一段視頻,學生點 擊小章節在線學習。
教學管理人員對課程計劃如何管理?
功能包括:添加課程計劃、刪除課程計劃、修改課程計劃等。
3.2課程計劃查詢
3.2.1需求分析
課程計劃查詢是將某個課程的課程計劃內容完整的顯示出來,如下圖所示:
左側顯示的就是課程計劃,課程計劃是一個樹型結構,方便擴展課程計劃的級別。 在上邊頁面中,點擊“添加課程計劃”即可對課程計劃進行添加操作。
點擊修改可對某個章節內容進行修改。
點擊刪除可刪除某個章節。
3.2.2頁面原型
3.2.2.1tree組件介紹
本功能使用element-ui 的tree組件來完成
在course_plan.vue文件中添加tree組件的代碼,進行測試:
1、組件標簽
<el‐tree :data="data" show‐checkbox node‐key="id" default‐expand‐all :expand‐on‐click‐node="false" :render‐content="renderContent"> </el‐tree>
2、數據對象
let id = 1000; export default { data() { return { data : [{ id: 1, label: '一級 1', children: [{ id: 4, label: '二級 1‐1', children: [{ id: 9, label: '三級 1‐1‐1' }, { id: 10, label: '三級 1‐1‐2' }] }] }] } } }
3.2.2.2webstorm配置JSX
本組件用到了JSX語法,如下所示:
JSX 是Javascript和XML結合的一種格式,它是React的核心組成部分,JSX和XML語法類似,可以定義屬性以及子元素。唯一特殊的是可以用大括號來加入JavaScript表達式。遇到 HTML 標簽(以 < 開頭),就用 HTML 規則解析;遇到代碼塊(以 { 開頭),就用 JavaScript 規則解析。
下面是官方的一個例子:
設置方法 如下:
設置方法 如下:
preferences -> Editor -> File Types 中找到上邊框中HTML 在下邊加一個 *.vue
3.2.3API接口
3.2.3.1數據模型
1、表結構
2、模型類
課程計劃為樹型結構,由樹根(課程)和樹枝(章節)組成,為了保證系統的可擴展性,在系統設計時將課程計劃 設置為樹型結構。
@Data @ToString @Entity @Table(name="teachplan") @GenericGenerator(name = "jpa‐uuid", strategy = "uuid") public class Teachplan implements Serializable { private static final long serialVersionUID = ‐916357110051689485L; @Id @GeneratedValue(generator = "jpa‐uuid") @Column(length = 32) private String id; private String pname; private String parentid; private String grade; private String ptype; private String description; private String courseid; private String status; private Integer orderby; private Double timelength; private String trylearn; }
3.2.3.2自定義模型類
前端頁面需要樹型結構的數據來展示Tree組件,如下:
[{ id: 1, label: '一級 1', children: [{ id: 4, label: '二級 1‐1' }] }]
自定義課程計劃結點類如下:
@Data @ToString public class TeachplanNode extends Teachplan { List<TeachplanNode> children; }
3.2.3.3接口定義
根據課程id查詢課程的計劃接口如下,在api工程創建course包,創建CourseControllerApi接口類并定義接口方法如下:
public interface CourseControllerApi { @ApiOperation("課程計劃查詢") public TeachplanNode findTeachplanList(String courseId); }
3.2.3課程管理服務
3.2.3.1Sql
課程計劃是樹型結構,采用表的自連接方式進行查詢,sql語句如下:
SELECT a.id one_id, a.pname one_pname, b.id two_id, b.pname two_pname, c.id three_id, c.pname three_pname FROM teachplan a LEFT JOIN teachplan b ON a.id = b.parentid LEFT JOIN teachplan c ON b.id = c.parentid WHERE a.parentid = '0' AND a.courseid = '402885816243d2dd016243f24c030002' ORDER BY a.orderby, b.orderby, c.orderby
3.2.3.2Dao
@Mapper public interface TeachplanMapper { public TeachplanNode selectList(String courseId); }
2)mapper映射文件
<resultMap type="com.xuecheng.framework.domain.course.ext.TeachplanNode" id="teachplanMap" > <id property="id" column="one_id"/> <result property="pname" column="one_name"/> <collection property="children" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode"> <id property="id" column="two_id"/> <result property="pname" column="two_name"/> <collection property="children" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode"> <id property="id" column="three_id"/> <result property="pname" column="three_name"/> </collection> </collection> </resultMap> <select id="selectList" resultMap="teachplanMap" parameterType="java.lang.String" > SELECT a.id one_id, a.pname one_name, b.id two_id, b.pname two_name, c.id three_id, c.pname three_name FROM teachplan a LEFT JOIN teachplan b ON a.id = b.parentid LEFT JOIN teachplan c ON b.id = c.parentid WHERE a.parentid = '0' <if test="_parameter!=null and _parameter!=''"> and a.courseid=#{courseId} </if> ORDER BY a.orderby, b.orderby, c.orderby </select>
說明:針對輸入參數為簡單類型#{}中可以是任意類型,判斷參數是否為空要用 _parameter(它屬于mybatis的內置參數)
3.4.3.3Service
創建CourseService類,定義查詢課程計劃方法。
@Service public class CourseService { @Autowired TeachplanMapper teachplanMapper; //查詢課程計劃 public TeachplanNode findTeachplanList(String courseId){ TeachplanNode teachplanNode = teachplanMapper.selectList(courseId); return teachplanNode; } }
3.4.3.4Controller
@RestController @RequestMapping("/course") public class CourseController implements CourseControllerApi { @Autowired CourseService courseService; //查詢課程計劃@Override @GetMapping("/teachplan/list/{courseId}") public TeachplanNode findTeachplanList(String courseId) { return courseService.findTeachplanList(courseId); } }
3.4.3.5測試
使用postman或swagger-ui測試查詢接口。
Get 請求:http://localhost:31200/course/teachplan/list/402885816243d2dd016243f24c030002
3.2.4前端頁面
3.2.4.1Api方法
定義課程計劃查詢的api方法:
/*查詢課程計劃*/ export const findTeachplanList = courseid => { return http.requestQuickGet(apiUrl+'/course/teachplan/list/'+courseid) }
3.2.4.2Api調用
1、在mounted鉤子方法 中查詢 課程計劃
定義查詢課程計劃的方法,賦值給數據對象teachplanList
findTeachplan(){ courseApi.findTeachplanList(this.courseid).then((res) => { this.teachplanList = [];//清空樹 if(res.children){ this.teachplanList = res.children; } });
2)在mounted鉤子中查詢課程計劃
mounted(){ //課程id this.courseid = this.$route.params.courseid; //課程計劃this.findTeachplan(); }
3)修改樹結點的標簽屬性
課程計劃信息中pname為結點的名稱,需要修改樹結點的標簽屬性方可正常顯示課程計劃名稱,如下:
defaultProps: { children: 'children', label: 'pname' }
3.2.4.3測試
3.3添加課程計劃
3.3.1需求分析
用戶操作流程:
1、進入課程計劃頁面,點擊“添加課程計劃”
2、打開添加課程計劃頁面,輸入課程計劃信息
上級結點說明:
不選擇上級結點表示當前課程計劃為該課程的一級結點。
當添加該課程在課程計劃中還沒有節點時要自動添加課程的根結點。
3、點擊提交。
3.3.1.1頁面原型說明
添加課程計劃采用彈出窗口組件Dialog。
1、視圖部分
在course_plan.vue頁面添加添加課程計劃的彈出窗口代碼:
<el‐dialog title="添加課程計劃" :visible.sync="teachplayFormVisible" > <el‐form ref="teachplayForm" :model="teachplanActive" label‐width="140px" style="width:600px;" :rules="teachplanRules" > <el‐form‐item label="上級結點" > <el‐select v‐model="teachplanActive.parentid" placeholder="不填表示根結點"> <el‐option v‐for="item in teachplanList" :key="item.id" :label="item.pname" :value="item.id"> </el‐option> </el‐select> </el‐form‐item> <el‐form‐item label="章節/課時名稱" prop="pname"> <el‐input v‐model="teachplanActive.pname" auto‐complete="off"></el‐input> </el‐form‐item> <el‐form‐item label="課程類型" > <el‐radio‐group v‐model="teachplanActive.ptype"> <el‐radio class="radio" label='1'>視頻</el‐radio> <el‐radio class="radio" label='2'>文檔</el‐radio> </el‐radio‐group> </el‐form‐item> <el‐form‐item label="學習時長(分鐘) 請輸入數字" > <el‐input type="number" v‐model="teachplanActive.timelength" auto‐complete="off" ></el‐ </el‐form‐item> <el‐form‐item label="排序字段" > <el‐input v‐model="teachplanActive.orderby" auto‐complete="off" ></el‐input> </el‐form‐item> <el‐form‐item label="章節/課時介紹" prop="description"> <el‐input type="textarea" v‐model="teachplanActive.description" ></el‐input> </el‐form‐item> <el‐form‐item label="狀態" prop="status"> <el‐radio‐group v‐model="teachplanActive.status" > <el‐radio class="radio" label="0" >未發布</el‐radio> <el‐radio class="radio" label='1'>已發布</el‐radio> </el‐radio‐group> </el‐form‐item> <el‐form‐item > <el‐button type="primary" v‐on:click="addTeachplan">提交</el‐button> <el‐button type="primary" v‐on:click="resetForm">重置</el‐button> </el‐form‐item> </el‐form> </el‐dialog>
2、數據模型
在數據模型中添加如下變量:
在 數 據 對 象 中 添 加 : teachplayFormVisible:false, teachplanRules: { pname: [ {required: true, message: '請輸入課程計劃名稱', trigger: 'blur'} ], status: [ {required: true, message: '請選擇狀態', trigger: 'blur'} ] }, teachplanActive:{},
3、 添加按鈕
通過變量teachplayFormVisible控制彈出窗口是否顯示。
<el‐button type="primary" @click="teachplayFormVisible = true">添加課程計劃</el‐button>
4、定義表單提交方法和重置方法
//提交課程計劃addTeachplan(){ alert() }, //重置表單resetForm(){ this.teachplanActive = {} },
3.3.3API接口
1)添加課程計劃
@ApiOperation("添加課程計劃") public ResponseResult addTeachplan(Teachplan teachplan);
3.3.4課程管理服務
3.3.3.1Dao
public interface TeachplanRepository extends JpaRepository<Teachplan, String> { //定義方法根據課程id和父結點id查詢出結點列表,可以使用此方法實現查詢根結點 public List<Teachplan> findByCourseidAndParentid(String courseId,String parentId); }
3.3.3.2Service
//獲取課程根結點,如果沒有則添加根結點 public String getTeachplanRoot(String courseId){ //校驗課程id Optional<CourseBase> optional = courseBaseRepository.findById(courseId); if(!optional.isPresent()){ return null; } CourseBase courseBase = optional.get(); //取出課程計劃根結點 List<Teachplan> teachplanList = teachplanRepository.findByCourseidAndParentid(courseId, if(teachplanList == null || teachplanList.size()==0){ //新增一個根結點 Teachplan teachplanRoot = new Teachplan(); teachplanRoot.setCourseid(courseId); teachplanRoot.setPname(courseBase.getName()); teachplanRoot.setParentid("0"); teachplanRoot.setGrade("1");//1級 teachplanRoot.setStatus("0");//未發布 teachplanRepository.save(teachplanRoot); return teachplanRoot.getId(); } Teachplan teachplan = teachplanList.get(0); return teachplan.getId(); } //添加課程計劃@Transactional public ResponseResult addTeachplan(Teachplan teachplan){ //校驗課程id和課程計劃名稱if(teachplan == null || StringUtils.isEmpty(teachplan.getCourseid()) || StringUtils.isEmpty(teachplan.getPname())){ ExceptionCast.cast(CommonCode.INVALIDPARAM); } //取出課程id String courseid = teachplan.getCourseid(); //取出父結點id String parentid = teachplan.getParentid(); if(StringUtils.isEmpty(parentid)){ //如果父結點為空則獲取根結點 parentid= getTeachplanRoot(courseid); } //取出父結點信息 Optional<Teachplan> teachplanOptional = teachplanRepository.findById(parentid); if(!teachplanOptional.isPresent()){ ExceptionCast.cast(CommonCode.INVALIDPARAM); } //父結點 Teachplan teachplanParent = teachplanOptional.get(); //父結點級別 String parentGrade = teachplanParent.getGrade(); //設置父結點teachplan.setParentid(parentid); teachplan.setStatus("0");//未發布 //子結點的級別,根據父結點來判斷 if(parentGrade.equals("1")){ teachplan.setGrade("2"); }else if(parentGrade.equals("2")){ teachplan.setGrade("3"); } // 設 置 課 程 id teachplan.setCourseid(teachplanParent.getCourseid()); teachplanRepository.save(teachplan); return new ResponseResult(CommonCode.SUCCESS); }
3.3.3.3controller
//添加課程計劃@Override @PostMapping("/teachplan/add") public ResponseResult addTeachplan(@RequestBody Teachplan teachplan) { return courseService.addTeachplan(teachplan); }
3.3.3.4測試
復雜一些的業務邏輯建議寫完服務端代碼就進行單元測試。使用swagger-ui或postman測試上邊的課程計劃添加接口。
3.3.5前端
3.3.5.1Api調用
1、定義 api方法
/*添加課程計劃*/ export const addTeachplan = teachplah => { return http.requestPost(apiUrl+'/course/teachplan/add',teachplah) }
2、調用 api
addTeachplan(){ this.$refs.teachplayForm.validate((valid) => { if (valid) { // 添 加 課 程 計 劃 時 帶 上 課 程 id this.teachplanActive.courseid = this.courseid; courseApi.addTeachplan(this.teachplanActive).then((res) => { if(res.success){ this.$message.success('提交成功'); //清空表單 this.teachplanActive = {} //刷新整個樹 this.findTeachplan(); }else{ this.$message.error('提交失敗'); } }); } }) },
3.3.5 測試
測試流程:
1、新建一個課程
2、向新建課程中添加課程計劃添加一級結點
*請認真填寫需求信息,我們會在24小時內與您取得聯系。