項目介紹:
實現的頁面效果:
后台管理頁面:
以上的靜態頁面,通過 HTML結構 和 CSS樣式 實現 而頁面的交互由 JS 來實現
練習項目的目標:
-
(1)前端三層分離的編程思想
-
-
(1)獨立小功能需求分析->頁面需求分析->整個項目需求分析
-
-
(1)項目技術難點的分析與解決方案
-
回顧模板引擎實現數據的動態渲染
掌握token實現狀態保持的前台頁面實現過程
進一步的掌握和體會前台頁面的交互,理解前台頁面交互的處理流程
項目之前需要使用數據可視化軟件 naVicat 和 mysql數據庫 將以存儲的數據導入和服務器的開啟
打開大事件項目文件夾 >> 找到大事件接口文件夾 >> 找到BigEventServers文件夾 >> 找到config 下的文件 index.js 打開 根據文件內容
根據以上內容,在Navicat 中新建一個名字叫 'bignews' 的數據庫 >> 然后在BigEventServers文件夾中找到 reset 文件打開 >> 在打開的該目錄下打開小黑窗 >> 輸入命令 node index.js 出現'搞定'兩個字即數據導入成功.
打開服務器: 在BigEventServers文件夾目錄下 打開小黑窗 輸入命令 app.js 運行 出現: 開啟成功: http://localhost:8080 即服務器打開成功.
1 <!-- 模態框做為body的直接子元素 --> 2 <div class="modal fade" id="myModal" tabindex="-1" role="dialog"> 3 <div class="modal-dialog" role="document"> 4 <div class="modal-content"> 5 <div class="modal-header"> 6 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span 7 aria-hidden="true">×</span></button> 8 <h4 class="modal-title">友情提示</h4> 9 </div> 10 <div class="modal-body"> 11 <p class="logininfo">One fine body…</p> 12 </div> 13 <div class="modal-footer"> 14 <button type="button" class="btn btn-primary btnconfirm">確定</button> 15 </div> 16 </div> 17 </div> 18 </div>
注意點: 模態框的整體結構要直接做 body 的子元素, 如果嵌套在其他HTML結構中會出現模態框無法點擊使用
1 (function (w) { 2 var baseURL = 'http://127.0.0.1:8080/api/v1' 3 w.itcast = { 4 // 定義基准路徑,所謂基准路徑就是資源url前面那一坨 5 baseURL: baseURL,//基地址 6 user_login: baseURL + '/admin/user/login',//用戶登錄 7 user_info: baseURL + '/admin/user/info',//用戶信息 8 user_detail: baseURL + '/admin/user/detail',//用戶詳情 9 user_edit: baseURL + '/admin/user/edit',//用戶編輯 10 category_list: baseURL + '/admin/category/list',//文章類別查詢 11 category_add: baseURL + '/admin/category/add',//文章類別新增 12 category_search: baseURL + '/admin/category/search',//文章類別搜索 13 category_edit: baseURL + '/admin/category/edit',//文章類別編輯 14 category_delete: baseURL + '/admin/category/delete',//文章類別刪除 15 article_query: baseURL + '/admin/article/query',//文章搜索 16 article_publish: baseURL + '/admin/article/publish',//文章發布 17 article_search: baseURL + '/admin/article/search',//文章信息查詢 18 article_edit: baseURL + '/admin/article/edit',//文章編輯 19 article_delete: baseURL + '/admin/article/delete',//文章刪除 20 comment_search: baseURL + '/admin/comment/search',//文章評論列表 21 comment_pass: baseURL + '/admin/comment/pass',//文章評論通過 22 comment_reject: baseURL + '/admin/comment/reject',//文章評論不通過 23 comment_delete: baseURL + '/admin/comment/delete',//文章評論刪除 24 } 25 })(window)
后台登錄頁面
實現點擊登錄按鈕, 登錄到后台首頁頁面
1 // 實現登陸 2 // 1、用戶登錄 3 // 請求地址:/admin/user/login 4 // 請求方式:post 5 $('.input_sub').on('click', function () { 6 // serialize:獲取獲取指定表單中擁有name屬性的表單元素的value值 7 console.log($('.login_form').serialize()) 8 $.ajax({ 9 type: 'post', 10 url: itcast.user_login, 11 data: $('.login_form').serialize(), 12 dataType: 'json', 13 success: function (res) { 14 console.log(res) 15 $('.logininfo').text(res.msg) 16 if (res.code == 200) { 17 // 將后台返回的token數據存儲到本地 18 localStorage.setItem('bignews_token_58', res.token) 19 $('#myModal').modal('show') 20 // hidden.bs.modal:在當前模態框被隱藏之后觸發 21 $('#myModal').on('hidden.bs.modal', function (e) { 22 location.href = './index.html' 23 }) 24 } else { 25 // alert(res.msg) 26 $('#myModal').modal('show') 27 } 28 } 29 }) 30 }) 31 32 // 單擊模態框中的確定,隱藏模態框 33 $('.btnconfirm').on('click', function () { 34 $('#myModal').modal('hide') 35 })
因為后續的頁面的操作都需要權限操作 , 即如果用戶登錄成功了才可以在后續的頁面中使用增刪改查的權限 , 那就需要給頁面傳一個tokan令牌,證明用戶已經登錄過了,即可繼續操作頁面,
因為后續的頁面有很多請求, 如果一個個去加既麻煩還冗余, 這時候就需要一個全局函數來解決這種
該函數可以寫在頁面引入的 jQuery文件 的 最后 不要加在中間或其他地方,容易更改了原代碼的結構
1 // 這里添加ajax的全局函數的配置 2 $.ajaxSetup({ 3 // 每個請求發送之前都會經過beforeSend,可以在這個回調進行請求頭的統一設置 4 beforeSend: function (xhr) { 5 let token = localStorage.getItem('bignews_token_58') 6 if (token) { 7 xhr.setRequestHeader('Authorization', token) 8 } 9 }, 10 // 添加統一的錯誤處理 11 error: function (xhr, status, error) { 12 console.log(xhr, status, error) 13 if (error == 'Forbidden') {//用戶未登錄 14 alert('請先登錄!') 15 window.location.href = './login.html';//跳轉登陸頁面 16 }; 17 } 18 })
注意事項:
在前端頁面結構中如果按鈕的類型為: 'submit' 會有一個默認行為,會影響到后面的操作, 所以將登陸按鈕的type屬性從submit修改為button
參數獲取不到,我們需要人為設置表單元素的name, 因為 serialize() 的方法會獲取到表單域中帶有name屬性的元素的表單值 name值一定要參照后台接口
token的本地存儲和傳遞
在登陸成功之后將token存儲到本地
后續請求傳遞token
為了簡化操作,使用$.ajaxSetup進行統一的處理
// 將后台返回的token數據存儲到本地 localStorage.setItem('bignews_token_58', res.token)
首頁
用戶信息的動態渲染
首頁主體內容頁面的展示
左側菜單的功能處理
頂部欄的個人中心和退出
// 動態渲染用戶信息 // 2、獲取用戶信息 // 請求地址:/admin/user/info // 請求方式:get $.ajax({ url: allSite.user_info, dataType: 'json', // beforeSend: function (xhr) { // xhr.setRequestHeader('Authorization', localStorage.getItem('token_58')) // }, success: function (res) { console.log(res); if (res.code == 200) { $('.user_info>img').attr('src', res.data.userPic) $('.user_info>span').html(`歡迎 ${res.data.nickname}`) $('.user_center_link>img').attr('src', res.data.userPic) } } })
首頁主體內容頁面的展示
使用方式
2.設置超鏈接的target屬性的值為iframe的name屬性的 值
<!-- 右側主體內容 --> <div class="main" id="main_body"> <!-- 添加一個浮動框架 --> <iframe class="myframe" src="./main_count.html" frameborder="0" name='main_frame'></iframe> </div>
1 //主頁左側導航欄功能實現 2 $('.level01').on('click', function () { 3 $(this).addClass('active').siblings().removeClass('active') 4 5 if ($(this).next().hasClass('level02')) { 6 $(this).next().slideToggle() 7 $(this).find('b').toggleClass('rotate0') 8 } else { 9 $('.level02').slideUp() 10 $('.level02>li').removeClass('active') 11 } 12 }) 13 14 $('.level02>li').on('click', function () { 15 $(this).addClass('active').siblings().removeClass('active') 16 })
后台首頁頂部的個人中心功能:
效果: 實現點擊個人中心跳轉到個人中心頁面,並給左側的導航欄的個人中心導航選項添加樣式
<a href="./user.html" target="main_frame" onclick='$("#user").click()'>個人中心</a>
后台首頁頂部的退出功能:
效果: 點擊退出 實現刪除之前存儲的token 並且跳轉到登錄頁面
//點擊退出按鈕實現刪除token令牌,跳轉到登錄頁面 $('.logout').on('click', function () { localStorage.removeItem('token_58') window.location.href = './login.html' })
文章列表頁
主要實現功能:
- 頁面數據的渲染,讓后台文章數據動態渲染到頁面中
- 數據篩選功能, 選擇文章類型和狀態,點擊篩選, 使頁面數據根據選項動態渲染
- 分頁插件, 在列表頁下方生成一個分頁結構,使用戶點擊對應鍵,跳轉到對應的頁面
- 實現發表文章 和 編輯文章 的頁面跳轉
頁面數據的渲染
使用模板引擎動態渲染頁面會更簡便
1 <!-- 頁面需要先引入模板引擎js文件 --> 2 <script src="./libs/template-web.js"></script> 3 ----------------------------------------------------- 4 5 <!-- 模板結構 --> 6 <script type="text/template" id="articleListTemp"> 7 {{each data}} 8 <tr> 9 <td>{{$value.title}}</td> 10 <td>{{$value.author}}</td> 11 <td>{{$value.category}}</td> 12 <td class="text-center">{{$value.date}}</td> 13 <td class="text-center">{{$value.state}}</td> 14 <td class="text-center"> 15 <a href="article_edit.html" class="btn btn-default btn-xs">編輯</a> 16 <a href="javascript:void(0);" class="btn btn-danger btn-xs delete">刪除</a> 17 </td> 18 </tr> 19 {{/each}} 20 </script>
實現動態渲染:
注意點: 在這里發起請求時 對應的傳參需要根據服務器接口文檔 所需的參數來傳遞
文檔所示所有的值可以為空, 則有默認值
// 獲取所有文章數據,實現文章數據的列表渲染
// 10、文章搜索
// 請求地址:/admin/article/query
// 請求方式:get
$.ajax({
url: itcast.article_query,
data: {
page: 1, // 當前頁碼
perpage: 10 // 每頁展示的數量
},
dataType: 'json',
success: function (res) {
console.log(res)
if (res.code == 200) {
$('tbody').html(template('articleListTemp', res.data))
}
}
})
數據篩選功能
難點: 下拉列表的使用
-
如果沒有設置value屬性,那么后期獲取到的select下拉列表的value屬性值為option標簽之間的值
-
如果設置了value屬性,那么后期獲取到的就是value屬性所綁定的值
下拉列表的使用場景
-
展示固定選項 -- 根據后台需求和展示需要固定寫死
-
渲染動態選項 -- 實現下拉列表的動態渲染,同時要根據數據業務的處理需求設置value屬性
// 分類數據的動態渲染
// 5、所有文章類別 // 請求地址:/admin/category/list // 請求方式:get
url: itcast.category_list, dataType: 'json', success: function (res) { console.log(res) if (res.code == 200) { let str = `<option value="">所有分類</option>` for (let i = 0; i < res.data.length; i++) { str += `<option value='${res.data[i].id}'>${res.data[i].name}</option>` } $('#selCategory').html(str) } } })
這時候會返回的數據
數據的篩選
-
-
對於文章分類,它的數據是來自於后台數據庫,所以需要動態渲染
- 對於后台的處理是:如果傳入的分類或狀態參數,就會拼接條件,否則不拼接條件,而查詢所有數據
function init () { $.ajax({ url: itcast.article_query, data: { page: 1, // 當前頁碼 perpage: 10, // 每頁展示的數量 type: $('#selCategory').val(), // 文章的分類,如果為''則查詢所有分類的文章數據 state: $('#selStatus').val() // 文章的狀態,如果為''則查詢所有狀態的文章數據 }, dataType: 'json', success: function (res) { console.log(res) if (res.code == 200) { $('tbody').html(template('articleListTemp', res.data)) } } }) }
// 實現數據篩選 $('#btnSearch').on('click', function (e) { e.preventDefault() page = 1 init() })
分頁插件 實現分頁結構
當數據量比較大的時候,不適合在一頁展示完所有數據的時候,就應該進行分頁
需要用到分頁插件 在Bootstrap官網中就提供了這種分頁插件
bootstrapMajorVersion 需要根據版本傳入對應的值
頁面需要引入的文件:
<link rel="stylesheet" href="./lib/bootstrap.css"> <!--使用bootstrap插件必須使用引入jquery,因為bootstrap是基於jquery開發的--> <script src="./lib/jquery-2.1.1.min.js"></script> <!--bootstrap插件--> <script src="./lib/bootstrap.js"></script> <!--分頁插件--> <script src="./lib/bootstrap-paginator.js"></script>
實現分頁結構
//實現分頁結構的顯示 /** * * @param pageCurrent 當前所在頁 因為有全局變量定義則不用傳遞 * @param pageSum 總頁數 * @param callback 調用ajax 不需要觸發 */ function setPage(pageSum) { $(".pagination").bootstrapPaginator({ //設置版本號 bootstrapMajorVersion: 3, // 顯示第幾頁 currentPage: page, // 總頁數 totalPages: pageSum, //當單擊操作按鈕的時候, 執行該函數, 調用ajax渲染頁面 onPageClicked: function (event, originalEvent, type, getpage) { // getpage 為第四個參數,獲取用戶點擊的頁碼數 console.log(getpage); //獲取的頁碼賦值給全局變量 page = getpage; // 再重新渲染頁面,跳轉到對應頁數 init() } }) }
打開發布文章頁面就開始渲染分類數據
這里要注意: 在頁面文件中要傳入封裝好的 http.js 文件 即 url 對應的地址
1 //打開發布文章,渲染頁面的文章類別 2 $.ajax({ 3 // 所有文章類別 4 // 請求地址:/ admin / category / list 5 // 請求方式:get 6 url: allSite.category_list, 7 dataType: 'json', 8 success: function (res) { 9 // console.log(res); 10 let str = '' 11 for (let index = 0; index < res.data.length; index++) { 12 str += `<option value="${res.data[index].id}">${res.data[index].name}</option>` 13 } 14 $('.category').html(str) 15 } 16 })
實現發布文章頁面的文件預覽
注意事項,因為使用的是formdata 方式上傳 即用戶選擇好圖片時就實現預覽但不上傳文件, 這時候就需要本地預覽
URL.creatObjectUrl:它可以將指定的資源托管到內置臨時服務器
1 /*2.文件預覽 */ 2 //1.給file表單元素注冊onchange事件 3 $('#inputCover').change(function () { 4 //1.2 獲取用戶選擇的圖片 通過URL的createObjectUrl獲取托管在服務器端的資源路徑 5 var file = $('#inputCover')[0].files[0]; 6 //1.3 將文件轉為src路徑 7 var url = window.URL.createObjectURL(file); 8 //1.4 將url路徑賦值給img標簽的src 9 $('.article_cover').attr('src', url); 10 });
實現發布文章頁面的時間功能
需要一個日期插件 在頁面文件中需要引入css 和 js 在頁面結構中還需要一個載體 建議使用文本框
引入文件
<link rel="stylesheet" href="./libs/jedate/css/jedate.css"> <script src="./libs/jedate/js/jedate.js"></script>
添加載體
<div class="form-group"> <label class="col-sm-2 control-label">發布時間:</label> <div class="col-sm-4">
--------添加一個文本框載體--------------- <input type="text" class="jeinput" id="testico" placeholder="YYYY-MM-DD" name='date'> </div> </div>
中有兩個參數, 第一個參數傳入載體, 第二參數傳的是一個對象,用於定制日期插件的功能樣式, 可以不傳 則有默認值,但樣式會很丑.
jeDate插件獲取日期值 用value方式就可以獲取
//加入日期插件 $(function () {
jeDate("#testico", { trigger: "click", format: "YYYY-MM-DD", theme: { bgcolor: "#D91600", pnColor: "#FF6653" }, isinitVal: true }) });
實現發布文章頁面
<script src="./libs/tinymce/tinymce.min.js"></script>
添加載體
<div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label">文章內容:</label> <div class="col-sm-10">
------------------添加載體------------
<textarea id="mytextarea"></textarea> </div> </div>
在頁面的js文件中 進行插件的初始化
注意事項:
- 富文本框插件會在載體上方添加一個新的結構,用戶的數據是輸入在這個新結構中,意味着我們無法從文本框載體中獲取數據
- 富文本框的內容是網頁結構,因為它要記錄用戶輸入內容的格式,而html可以描述這種結構
//加入富文本框插件 tinymce.init({ selector: '#mytextarea', height: '350px', language: 'zh_CN', directionality: 'ltl', browser_spellcheck: true, contextmenu: false, plugins: [ "advlist autolink lists link image charmap print preview anchor", "searchreplace visualblocks code fullscreen", "insertdatetime media table contextmenu paste imagetools wordcount", "code" ], toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | code", });
獲取插件的內容的方法: tinymce.activeEditor.getContent()
再將數據添加到 formdata 中
let formdata = new FormData($('#form')[0]) // console.log(tinymce.activeEditor.getContent()) // 將富文本框的內容添加到formdata formdata.append('content', tinymce.activeEditor.getContent())
實現文章的新增
下面是完整的實現代碼 其中沒有 分類的動態渲染和插件的使用 的代碼 是因為后期還需要復用到 所以封裝到一個js文件中去引入使用 則在頁面HTML文件中引入即可,下方有詳細代碼
注意事項:
- 使用formdata方式獲取數據時 會少了 富文本框的數據 和 文章狀態的數據 就需要主動添加進去
- 發布文章的圖片一定要上傳, 因為后台接口是固定需要不可以為空 ,不傳遞會報400的錯誤 即參數不正確
- 圖片的格式支持jpg,png,gif,擴展名要求小寫
1 $(function () { 2 /*2.文件預覽 */ 3 //1.給file表單元素注冊onchange事件 4 $('#inputCover').change(function () { 5 //1.2 獲取用戶選擇的圖片 6 var file = $('#inputCover')[0].files[0]; 7 //1.3 將文件轉為src路徑 通過URL的createObjectUrl獲取托管在服務器端的資源路徑 8 var url = window.URL.createObjectURL(file); 9 //1.4 將url路徑賦值給img標簽的src 10 $('.article_cover').attr('src', url); 11 }); 12 //點擊發布按鈕,獲取用戶數據 13 // 11、發布文章 14 // 請求地址:/admin/article / publish 15 // 請求方式:post 16 // 請求參數:通過formData提交 17 // 由於發布按鈕和草稿按鈕業務邏輯相同, 18 // 只是state參數不同,可以封裝一個函數,將state作為參數傳遞 19 $('.btn-release').on('click', function (e) { 20 //阻止默認行為 21 e.preventDefault() 22 uploadingData('已發布') 23 }) 24 $('.btn-draft').on('click', function (e) { 25 //阻止默認行為 26 e.preventDefault() 27 uploadingData('草稿') 28 }) 29 30 function uploadingData(state) { 31 //創建FormData 32 let formdata = new FormData($('#form')[0]) 33 //tinymce.activeEditor.getContent()獲取富文本框的文本內容 獲取的數據是以 HTML格式顯示 ,后期渲染時需要使用html方法 34 formdata.append('content', tinymce.activeEditor.getContent()) 35 //追加文章狀態 36 formdata.append('state', state) 37 console.log(...formdata); 38 //實現文章發布 39 $.ajax({ 40 type: 'post', 41 url: allSite.article_publish, 42 data: formdata, 43 dataType: 'json', 44 contentType: false, 45 processData: false, 46 success: function (res) { 47 console.log(res); 48 if (res.code == 200) { 49 alert(res.msg) 50 //返回文章列表頁 ,給左側列表頁導航欄項添加樣式 51 $('.level02>li:eq(0)', window.parent.document).addClass('active').siblings().removeClass('active'); 52 window.location.href = './article_list.html' 53 } 54 } 55 }) 56 } 57 58 })
公共代碼 comment.js
1 $(function () { 2 //打開發布文章,渲染頁面的文章類別 3 $.ajax({ 4 // 所有文章類別 5 // 請求地址:/ admin / category / list 6 // 請求方式:get 7 url: allSite.category_list, 8 dataType: 'json', 9 success: function (res) { 10 // console.log(res); 11 let str = '' 12 for (let index = 0; index < res.data.length; index++) { 13 str += `<option value="${res.data[index].id}">${res.data[index].name}</option>` 14 } 15 $('.category').html(str) 16 } 17 }) 18 19 //加入日期插件 20 $(function () { 21 //或者為這樣的 22 jeDate("#testico", { 23 trigger: "click", 24 format: "YYYY-MM-DD", 25 theme: { bgcolor: "#D91600", pnColor: "#FF6653" }, 26 isinitVal: true 27 }) 28 }); 29 30 //加入富文本框插件 31 tinymce.init({ 32 selector: '#mytextarea', 33 height: '350px', 34 language: 'zh_CN', 35 directionality: 'ltl', 36 browser_spellcheck: true, 37 contextmenu: false, 38 plugins: [ 39 "advlist autolink lists link image charmap print preview anchor", 40 "searchreplace visualblocks code fullscreen", 41 "insertdatetime media table contextmenu paste imagetools wordcount", 42 "code" 43 ], 44 toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | code", 45 }); 46 })
文章刪除功能
注意事項:
- 在文章列表頁面中 文章的數據時使用模板引擎動態渲染的 所有在綁定事件時 不可以直接綁定在元素上, 要通過結構中存在的父元素來綁定--事件委托的方式
- 刪除事件需要根據id來進行刪除 而結構中又沒有id參數傳遞 這時候就需要自己添加 自定義屬性 data 來存儲id 再去獲取id
在模板結構的刪除結構 添加自定義屬性
1 <script type="text/template" id="articleListTemp"> 2 {{each data}} 3 <tr> 4 <td>{{$value.title}}</td> 5 <td>{{$value.author}}</td> 6 <td>{{$value.category}}</td> 7 <td class="text-center">{{$value.date}}</td> 8 <td class="text-center">{{$value.state}}</td> 9 <td class="text-center"> 10 <a href="article_edit.html?id={{$value.id}}" class="btn btn-default btn-xs" id="compile_btn">編輯</a>
--------------添加自定義屬性--------------------------- 11 <a href="javascript:void(0);" class="btn btn-danger btn-xs delete_btn" data-id='{{$value.id}}'>刪除</a> 12 </td> 13 </tr> 14 {{/each}}
實現刪除效果
注意獲取頁面傳遞的id
1 //實現刪除文章功能 2 // 14、刪除文章 3 // 請求地址:/admin/article / delete 4 // 請求方式:post 5 $('table>tbody').on('click', '.delete_btn', function () { 6 $.ajax({ 7 type: 'post', 8 url: allSite.article_delete, 9 dataType: 'json', 10 data: {
--------------獲取頁面傳遞的id------------- 11 id: $(this).attr('data-id') 12 }, 13 success: function (res) { 14 console.log(res); 15 if (res.code == 204) { 16 alert(res.msg) 17 location.reload(); 18 } else { 19 alert(res.msg) 20 } 21 } 22 }) 23 })
文章編輯功能
用戶點擊編輯, 跳轉到編輯頁面 根據后台數據渲染 編輯文章頁面
渲染頁面注意事項:
- 在編輯跳轉時要傳遞id
- 在編輯頁接收參數id
- 根據id查詢文章詳情數據
- dom賦值
實現數據的默認展示
傳遞id
<script type="text/template" id="articleListTemp"> {{each data}} <tr> <td>{{$value.title}}</td> <td>{{$value.author}}</td> <td>{{$value.category}}</td> <td class="text-center">{{$value.date}}</td> <td class="text-center">{{$value.state}}</td> <td class="text-center">
-------------添加?id={{$value.id}} 用於傳遞id-----------------------
<a href="article_edit.html?id={{$value.id}}" class="btn btn-default btn-xs" id="compile_btn">編輯</a> <a href="javascript:void(0);" class="btn btn-danger btn-xs delete_btn" data-id='{{$value.id}}'>刪除</a> </td> </tr> {{/each}}
在編輯頁面獲取id
使用 itcast.getParameter 需要在頁面文件中引入一個js文件 用於把傳遞的參數結構為 key=value&key=value&key=value.... 轉換為對象
//1.獲取artile_list頁面傳遞過來的文章id let id = itcast.getParameter(location.search).id // console.log(id);
引入的文件結構 可以創建文件再復制粘貼代碼使用
1 var itcast = { 2 getParameter: function (str) { // ?id=7&name=jack 3 // 刪除? 4 str = str.replace('?', '') // id=7&name=jack 5 // 分割字符串 6 var arr = str.split('&') // ["id=7","name=jack"] 7 // 循環遍歷再次分割 8 var obj = {} 9 for (var i = 0; i < arr.length; i++) { // 1.id=7 10 var temp = arr[i].split('=') // ["id",7] 11 // 將數據添加到對象 12 obj[temp[0]] = temp[1] // {id:7} 13 } 14 return obj 15 } 16 }
- 富文本框的賦值 插件默認會將文本域的內容同步到富文本框插件,當插件加載完畢之后同步
1 //根據id獲取文章信息 2 // 12、根據id獲取文章信息 3 // 請求地址:/admin/article / search 4 // 請求方式:get 5 6 //1.獲取artile_list頁面傳遞過來的文章id 7 let id = itcast.getParameter(location.search).id 8 // console.log(id); 9 $.ajax({ 10 url: allSite.article_search, 11 data: { id }, 12 dataType: 'json', 13 success: function (res) { 14 // console.log(res); 15 if (res.code == 200) { 16 $('#inputTitle').val(res.data.title) 17 $('.article_cover').attr('src', res.data.cover)
// settimeout中的代碼,會在其它所有代碼執行完畢之后再執行 18 setTimeout(function () { 19 $('select.category').val(res.data.categoryId) 20 }, 0) 21 $('#testico').val(res.data.date)
//富文本框的賦值 插件默認會將文本域的內容同步到富文本框插件,當插件加載完畢之后同步
22 $('#mytextarea').val(res.data.content) 23 } 24 } 25 })
1 $(function () { 2 //根據id獲取文章信息 3 // 12、根據id獲取文章信息 4 // 請求地址:/admin/article / search 5 // 請求方式:get 6 //1.獲取artile_list頁面傳遞過來的文章id 7 let id = itcast.getParameter(location.search).id 8 // console.log(id); 9 $.ajax({ 10 url: allSite.article_search, 11 data: { id }, 12 dataType: 'json', 13 success: function (res) { 14 // console.log(res); 15 if (res.code == 200) { 16 $('#inputTitle').val(res.data.title) 17 $('.article_cover').attr('src', res.data.cover) 18 setTimeout(function () { 19 $('select.category').val(res.data.categoryId) 20 }, 0) 21 $('#testico').val(res.data.date) 22 $('#mytextarea').val(res.data.content) 23 } 24 } 25 }) 26 // 13、文章編輯 27 // 請求地址:/admin/article / edit 28 // 請求方式:post 29 $('.btn-edit').on('click', function (e) { 30 //阻止默認行為 31 e.preventDefault() 32 uploadingData('已發布') 33 }) 34 $('.btn-draft').on('click', function (e) { 35 //阻止默認行為 36 e.preventDefault() 37 uploadingData('草稿') 38 }) 39 40 function uploadingData(state) { 41 //創建FormData 42 let formdata = new FormData($('#form')[0]) 43 //tinymce.activeEditor.getContent()獲取富文本框的文本內容 獲取的數據是以 HTML格式顯示 44 formdata.append('content', tinymce.activeEditor.getContent()) 45 //追加文章狀態 46 formdata.append('state', state) 47 //追加id 48 formdata.append('id', id) 49 // console.log(...formdata); 50 //實現文章發布 51 $.ajax({ 52 type: 'post', 53 url: allSite.article_edit, 54 data: formdata, 55 dataType: 'json', 56 contentType: false, 57 processData: false, 58 success: function (res) { 59 console.log(res); 60 if (res.code == 200) { 61 alert('修改成功') 62 //返回文章列表頁 ,給左側列表頁導航欄項添加樣式 63 $('.level02>li:eq(0)', window.parent.document).addClass('active').siblings().removeClass('active'); 64 window.location.href = './article_list.html' 65 } 66 } 67 }) 68 } 69 /*2.文件預覽 */ 70 //1.給file表單元素注冊onchange事件 71 $('#inputCover').change(function () { 72 //1.2 獲取用戶選擇的圖片 73 var file = $('#inputCover')[0].files[0]; 74 //1.3 將文件轉為src路徑 75 var url = window.URL.createObjectURL(file); 76 //1.4 將url路徑賦值給img標簽的src 77 $('.article_cover').attr('src', url); 78 }); 79 })
<link rel="stylesheet" href="./libs/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="css/reset.css"> <link rel="stylesheet" href="css/iconfont.css"> <link rel="stylesheet" href="css/main.css"> <script src="./libs/jquery-1.12.4.min.js"></script> <script src="./libs/bootstrap/js/bootstrap.min.js"></script> <script src="./js/http.js"></script> <!-- 引入模板引擎js文件 --> <script src="./libs/template-web.js"></script> <script src="./js/article_category.js"></script>
1 <!-- 模板結構 --> 2 <script type="text/template" id="cataTemp"> 3 {{each data}} 4 <tr> 5 <td>{{$value.name}}</td> 6 <td>{{$value.slug}}</td> 7 <td class="text-center"> 8 <!-- 加入自定義屬性 用於存儲數據,為編輯所需的數據做准備 --> 9 <a href="javascript:void(0)" data-toggle="modal" class="btn btn-info btn-xs btnedit" data-id="{{$value.id}}" data-name="{{$value.name}}" data-slug="{{$value.slug}}">編輯</a> 10 <a href="javascript:void(0)" class="btn btn-danger btn-xs btndel" data-id="{{$value.id}}">刪除</a> 11 </td> 12 </tr> 13 {{/each}} 14 </script>
// 5、所有文章類別 // 請求地址:/admin/category / list // 請求方式:get let paradata = [] let id //設置全局變量 用於獲取當前編輯的分類數據的id function init() { $.ajax({ url: allSite.category_list, dataType: 'json', success: function (res) { // console.log(res); if (res.code == 200) { paradata = res.data $('tbody').html(template('cataTemp', res)) } } }) } init()
文章類別的新增
這里因為新增文章類別的信息特別的少,所以沒有必要去做一個新頁面來做新增,而可以選擇使用 模態框 或者 在頁面的某個位置單獨的划出一塊區域進行新增操作
-
可以使用js的方式--最終選擇它來實現
-
1 <!-- 模態框的添加 --> 2 <div class="modal fade" tabindex="-1" role="dialog" id="myModal"> 3 <div class="modal-dialog" role="document"> 4 <div class="modal-content"> 5 <div class="modal-header"> 6 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span 7 aria-hidden="true">×</span></button> 8 <h4 class="modal-title">新增分類</h4> 9 </div> 10 <div class="modal-body"> 11 <form action="" id="form"> 12 <div class="form-group"> 13 <label for="name">分類名稱</label> 14 <input type="text" class="form-control" id="name" name="name" placeholder="分類名稱"> 15 </div> 16 <div class="form-group"> 17 <label for="slug">分類別名</label> 18 <input type="text" class="form-control" id="slug" name="slug" placeholder="分類別名"> 19 </div> 20 </form> 21 </div> 22 <div class="modal-footer"> 23 <button type="button" class="btn btn-default" data-dismiss="modal">取消</button> 24 <button type="button" class="btn btn-primary btnset">新增</button> 25 </div> 26 </div> 27 </div> 28 </div>
-
獲取到當前的所有分類數據
-
在用戶輸入失焦的時候驗證用戶是否存在,生成一個標記
-
-
在查詢到所有的分類數據之后,將分類數據存儲到變量,供后期的判斷使用
-
在每次用戶輸入完值之后,使用之前存儲的分類數據做為參照,查詢當前用戶的輸入是否已存在
-
1 //驗證用戶輸入 2 let flag = true //標記當前請求是否要發送 3 $('#name').on('blur', function () { 4 let value = $(this).val() 5 let temp = paradata.filter(v => { 6 return v.name == value 7 })[0] 8 if (temp) { 9 alert('分類名稱重復,請重新輸入') 10 flag = false 11 } else { 12 flag = true 13 } 14 }) 15 $('#slug').on('blur', function () { 16 let value = $(this).val() 17 let temp = paradata.filter(v => { 18 return v.slug == value 19 })[0] 20 if (temp) { 21 alert('分類別名重復,請重新輸入') 22 flag = false 23 } else { 24 flag = true 25 } 26 })
-
-
如果只是普通的鍵值對那么就使用serialize
-
類別的編輯
編輯都需要完成兩個功能
1.數據的默認展示
2.編輯功能
數據的默認展示
-
如果是單獨的頁面實現編輯,那么就需要傳遞參數,獲取參數,查詢數據,默認展示
-
頁面跳轉是get請求,get無法傳遞太多的數據,且傳遞數據的方式不安全
-
所以這種情況下一般傳遞id,到編輯頁面進行數據的查詢,再展示
-
-
如果是當前頁面實現編輯操作(彈框,單獨區域),一般來說編輯的數據比較的簡單
-
由於沒有跳轉頁面,那么意味着本質上沒有必須進行參數的傳遞
-
所以一般情況下這種場景可以先存儲數據,再獲取數據進行展示
-
如果沒有特別的需要,可以考慮使用自定義屬性
-
<td class="text-center"> <!-- 加入自定義屬性 用於存儲數據,為編輯所需的數據做准備 --> <a href="javascript:void(0)" data-toggle="modal" class="btn btn-info btn-xs btnedit" data-id="{{$value.id}}" data-name="{{$value.name}}" data-slug="{{$value.slug}}">編輯</a> <a href="javascript:void(0)" class="btn btn-danger btn-xs btndel" data-id="{{$value.id}}">刪除</a> </td>
在編輯事件中獲取自定義屬性,實現展示. 注意: 因為文章類別是使用模板結構動態渲染的, 要使用事件委托的方式來觸發事件, 獲取自定義屬性 , 彈出模態框 ,wei元素賦值
編輯分類
-
編輯和新增是復用同一個模態框
- 編輯過后的新增,需要將數據,提示信息重置
-
可以根據文本內容或者id進行判斷,決定是進行編輯還是新增
-
編輯和新增的業務是一樣,可以進行業務的封裝
1 $(function () { 2 // 5、所有文章類別 3 // 請求地址:/admin/category / list 4 // 請求方式:get 5 let paradata = [] 6 let id //設置全局變量 用於獲取當前編輯的分類數據的id 7 function init() { 8 $.ajax({ 9 url: allSite.category_list, 10 dataType: 'json', 11 success: function (res) { 12 // console.log(res); 13 if (res.code == 200) { 14 paradata = res.data 15 $('tbody').html(template('cataTemp', res)) 16 } 17 } 18 }) 19 } 20 init() 21 22 //驗證用戶輸入 23 let flag = true //標記當前請求是否要發送 24 $('#name').on('blur', function () { 25 let value = $(this).val() 26 let temp = paradata.filter(v => { 27 return v.name == value 28 })[0] 29 if (temp) { 30 alert('分類名稱重復,請重新輸入') 31 flag = false 32 } else { 33 flag = true 34 } 35 }) 36 $('#slug').on('blur', function () { 37 let value = $(this).val() 38 let temp = paradata.filter(v => { 39 return v.slug == value 40 })[0] 41 if (temp) { 42 alert('分類別名重復,請重新輸入') 43 flag = false 44 } else { 45 flag = true 46 } 47 }) 48 // 6、新增文章類別 49 // 請求地址:/admin/category / add 50 // 請求方式:post 51 52 //封裝函數 因為新增功能 和 編輯功能 都需要重新渲染頁面數據 ,可以用函數封裝,再傳遞參數 53 function operate(url, data) { 54 $.ajax({ 55 //ajax支持三種格式的參數傳遞 56 url: url, 57 dataType: 'json', 58 data: data, 59 type: 'post', 60 success: function (res) { 61 // console.log(res); 62 if (res.code == 201 || res.code == 200) { 63 alert(res.msg) 64 $('#myModal').modal('hide') 65 init() 66 } 67 } 68 }) 69 } 70 71 72 73 //點擊新增按鈕彈出模態框 74 $('#xinzengfenlei').on('click', function () { 75 //因為新增功能和編輯功能是共用一個模態框,所有模態框的值需要動態渲染,點擊不同功能顯示不同的文本信息 76 //新增的提示信息 77 $('.modal-title').text('新增分類') 78 $('.btnset').text('新增') 79 //彈出模態框 80 $('#myModal').modal('show') 81 //新增功能的輸入框內容為空 82 $('#name').val('') 83 $('#slug').val('') 84 }) 85 86 //編輯功能時模態框展示的數據 87 $('tbody').on('click', '.btnedit', function () { 88 //獲取自定義的屬性 89 let obj = $(this).data() 90 // console.log(obj); 91 //修改模態框提示信息 92 $('.modal-title').text('編輯分類') 93 $('.btnset').text('編輯') 94 //彈出模態框 95 $('#myModal').modal('show') 96 //編輯功能的默認值 97 id = obj.id 98 $('#name').val(obj.name) 99 $('#slug').val(obj.slug) 100 }) 101 102 //這是點擊模態框確認鍵的觸發的事件 103 $('.btnset').on('click', function () { 104 if (flag = false) { 105 return 106 } 107 let obj = $('#form').serialize() 108 // console.log(obj); 109 if ($(this).text() == '新增') { 110 operate(allSite.category_add, obj) 111 } else if ($(this).text() == '編輯') { 112 operate(allSite.category_edit, obj + '&id=' + id) 113 } 114 }) 115 116 //刪除文章分類 117 // 9、刪除文章類別 118 // 請求地址:/admin/category / delete 119 // 請求方式:post 120 121 //使用模態引擎動態生成的結構需要使用事件委托來觸發事件 122 $('tbody').on('click', '.btndel', function () { 123 let id = $(this).data().id 124 // console.log(id); 125 $.ajax({ 126 url: allSite.category_delete, 127 type: 'post', 128 dataType: 'json', 129 data: { id }, 130 success: function (res) { 131 // console.log(res); 132 if (res.code == 204) { 133 alert(res.msg) 134 init() 135 } else { 136 alert(res.msg) 137 } 138 } 139 }) 140 }) 141 142 })
1 <!-- 模板結構 --> 2 <script type="text/template" id="comTemp"> 3 {{each data}} 4 <tr> 5 <td>{{$value.author}}</td> 6 <td>{{$value.content}}</td> 7 <td>{{$value.title}}</td> 8 <td>{{$value.date}}</td> 9 <td>{{$value.state}}</td> 10 <td class="text-center"> 11 {{if $value.state == '待審核'}} 12 <a href="#" class="btn btn-success btn-xs btnRatify" data-id="{{$value.id}}">批准</a> 13 {{else if $value.state == '已通過'}} 14 <a href="#" class="btn btn-warning btn-xs btnReject" data-id="{{$value.id}}">拒絕</a> 15 {{/if}} 16 <a href="#" class="btn btn-danger btn-xs btnDel" data-id="{{$value.id}}">刪除</a> 17 </td> 18 </tr> 19 {{/each}} 20 </script>
實現 批准 拒絕 刪除 等功能
1 $(function () { 2 3 //動態渲染評論數據 4 // 19、文章評論搜索 5 // 請求地址:/admin/comment / search 6 // 請求方式:get 7 let page = 1, perpage = 10 8 function init() { 9 $.ajax({ 10 url: allSite.comment_search, 11 dataType: 'json', 12 data: { 13 page, 14 perpage 15 }, 16 success: function (res) { 17 // console.log(res); 18 if (res.code == 200) { 19 $('tbody').html(template('comTemp', res.data)) 20 //給分頁函數傳參 生成分頁結構 21 setPage(res.data.totalPage) 22 } 23 } 24 }) 25 } 26 init() 27 28 //添加分頁插件 29 //實現分頁結構的顯示 30 /** 31 * 32 * @param pageCurrent 當前所在頁 33 * @param pageSum 總頁數 34 * @param callback 調用ajax 35 */ 36 function setPage(pageSum) { 37 $("#pagination").bootstrapPaginator({ 38 //設置版本號 39 bootstrapMajorVersion: 3, 40 // 顯示第幾頁 41 currentPage: page, 42 // 總頁數 43 totalPages: pageSum, 44 //當單擊操作按鈕的時候, 執行該函數, 調用ajax渲染頁面 45 onPageClicked: function (event, originalEvent, type, getpage) { 46 // getpage 為第四個參數,獲取用戶點擊的頁碼數 47 // console.log(getpage); 48 //獲取的頁碼賦值給全局變量 49 page = getpage; 50 // 再重新渲染頁面,跳轉到對應頁數 51 init() 52 } 53 }) 54 } 55 56 //批准 拒絕 刪除 評論的操作 封裝函數 57 function comopt(id, url) { 58 $.ajax({ 59 url: url, 60 type: 'post', 61 dataType: 'json', 62 data: { id }, 63 success: function (res) { 64 // console.log(res); 65 if (res.code == 200) { 66 alert(res.msg) 67 init(); 68 } 69 } 70 }) 71 } 72 //點擊批准 實現審核通過 73 // 20、評論審核通過 74 // 請求地址:/admin/comment / pass 75 // 請求方式:post 76 $('tbody').on('click', '.btnRatify', function () { 77 let id = $(this).data().id 78 // console.log(id); 79 comopt(id, allSite.comment_pass) 80 }) 81 82 //點擊拒絕 實現審核不通過 83 // 21、評論審核不通過 84 // 請求地址:/admin/comment / reject 85 // 請求方式:post 86 $('tbody').on('click', '.btnReject', function () { 87 let id = $(this).data().id 88 // console.log(id); 89 comopt(id, allSite.comment_reject) 90 }) 91 92 //點擊刪除 刪除評論數據 93 // 22、刪除評論 94 // 請求地址:/admin/comment / delete 95 // 請求方式:post 96 97 $('tbody').on('click', '.btnDel', function () { 98 let id = $(this).data().id 99 console.log(id); 100 if ($('tbody').find('tr').length == 1) { 101 if (page > 1) { 102 page-- 103 } 104 } 105 comopt(id, allSite.comment_delete) 106 107 }) 108 })
1 $(function () { 2 //當頁面一加載就開始渲染頁面 3 //渲染個人中心頁 4 function init() { 5 $.ajax({ 6 // 請求地址:/ admin / user / detail 7 // 請求方式:get 8 // 請求參數:無 9 url: allSite.user_detail, 10 dataType: 'json', 11 success: function (res) { 12 console.log(res); 13 for (let key in res.data) { 14 $('input.' + key).val(res.data[key]) 15 } 16 $('.col-sm-10>.user_pic').attr('src', res.data.userPic) 17 } 18 }) 19 } 20 init() 21 22 //實現個人中心頁用戶上傳圖片預覽功能 23 $('#exampleInputFile').change(function () { 24 //1.2 獲取用戶選擇的圖片 25 // var file = this.files[0]; 兩種方式都可以獲取 26 let file = $(this)[0].files[0] //兩種方式都可以獲取 27 //1.3 將文件轉為src路徑 28 let url = URL.createObjectURL(file); 29 //1.4 將url路徑賦值給img標簽的src 30 $('.user_pic').attr('src', url); 31 }); 32 33 34 //實現個人中心頁的用戶編輯 點擊頁面修改上傳修改信息 35 // 4、編輯用戶信息 36 // 請求地址:/admin/user / edit 37 // 請求方式:post 38 // 請求數據:使用formData提交 39 // 當前頁面所在的窗口window對象是user.html, 如果想要獲取父窗口,則需要使用window.parent,這種用法一般用於獲取iframe標簽的父窗口。 40 //submit 上傳事件 41 $('form').on('submit', function (e) { 42 e.preventDefault(); 43 $.ajax({ 44 url: allSite.user_edit, 45 data: new FormData(this), 46 type: 'post', 47 dataType: 'json', 48 contentType: false, 49 processData: false, 50 success: function (res) { 51 console.log(res); 52 // console.log(window); 53 if (res.code == 200) { 54 // window.parent.location.reload() 55 alert('修改成功') 56 window.parent.location.reload() 57 } 58 } 59 }) 60 }) 61 })