大事件后台管理系统开发实战(下)
文章目錄
- 續前篇:大事件后臺管理系統開發實戰(中)
- 1. 文章類別
- 1.1 點擊編輯按鈕展示修改文章分類的彈出層
- 1.2 為修改文章分類的彈出層填充表單數據
- 1.3 更新文章分類的數據
- 1.4 刪除文章分類
- 2. 文章列表
- 2.1 創建文章列表頁面
- 2.2 定義查詢參數對象q
- 2.3 請求文章列表數據并使用模板引擎渲染列表結構
- 2.3.1 定義獲取文章列表數據
- 2.4 定義美化時間格式的過濾器
- 2.5 繪制篩選區域的UI結構
- 2.6 發起請求獲取并渲染文章分類的下拉選擇框
- 2.7 實現篩選的功能
- 3. 分頁
- 3.1 定義渲染分頁的 renderPage 方法
- 3.2 調用 laypage.render 方法渲染分頁的基本結構
- 3.3 在jump回調函數中通過obj.curr獲取到最新的頁碼值
- 3.4 解決 jump 回調函數發生死循環的問題
- 3.5 自定義分頁的功能項
- 3.6 實現切換每頁展示多少條數據的功能
- 4. 刪除文章
- 4.1 實現刪除文章的功能
- 4.2 解決刪除文章時的小 Bug
- 5. 發布文章
- 5.1 創建文章發布頁面的基本結構
- 5.2 新建基本的表單結構
- 5.3 渲染文章類別對應的下拉選擇框結構
- 5.4 渲染富文本編輯器
- 5.5 渲染封面裁剪區域
- 5.6 渲染提交按鈕區域
- 5.7 點擊選擇封面按鈕打開文件選擇框
- 5.8 將選擇的圖片設置到裁剪區域中
- 小結 - 渲染封面裁剪區域
- 5.9 分析發布文章的實現步驟
- 5.10 基于Form表單快速創建FormData對象
- 5.11 將裁剪后的封面追加到FormData對象中
- 5.12 發起Ajax請求實現發布文章的功能
- 6. 編輯文章
- 6.1 創建文章編輯頁面基本結構
- 6.2 給編輯按鈕添加點擊事件
- 6.3 新建基本的表單結構
- 6.4 文章類別對應的下拉選擇框結構
- 6.5 渲染富文本編輯器
- 6.6 封面裁剪區域結構
- 6.7 提交按鈕區域
- 6.8 通過 URLSearchParams 對象,獲取 URL 傳遞的參數
- 6.9 發起請求獲取文章詳情
- 6.10 選擇文章封面
- 6.11 監聽文件(文章封面圖片)選擇框的 change 事件
- 6.12 設置文章的發布狀態
- 6.13 發布文章
- 6.14 完整html結構
- 6.15 完整css 樣式代碼
- 6.16 編輯功能完整 JS 代碼
- 7. 將開發完成的項目代碼推送到GitHub
續前篇:大事件后臺管理系統開發實戰(中)
1. 文章類別
1.1 點擊編輯按鈕展示修改文章分類的彈出層
為編輯按鈕添加 btn-edit 類名如下:
<button type="button" class="layui-btn layui-btn-xs btn-edit" data-id="{{$value.Id}}">編輯</button>定義 修改分類 的彈出層:
<script type="text/html" id="dialog-edit"><form class="layui-form" id="form-edit" lay-filter="form-edit"><!-- 隱藏域,保存 Id 的值 --><input type="hidden" name="Id"><div class="layui-form-item"><label class="layui-form-label">分類名稱</label><div class="layui-input-block"><input type="text" name="name" required lay-verify="required" placeholder="請輸入分類名稱" autocomplete="off" class="layui-input"></div></div><div class="layui-form-item"><label class="layui-form-label">分類別名</label><div class="layui-input-block"><input type="text" name="alias" required lay-verify="required" placeholder="請輸入分類別名" autocomplete="off" class="layui-input"></div></div><div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn" lay-submit lay-filter="formDemo">確認修改</button></div></div></form> </script>快速為一個表單填充數據:先為form表單添加 lay-filter 屬性,再調用form方法(前提是要先導入form:var form=layui.form)。同時,為表單創建一個隱藏域(input)用于保存id值
3. 通過 代理 的形式,為 btn-edit 按鈕綁定點擊事件:
1.2 為修改文章分類的彈出層填充表單數據
為編輯按鈕綁定 data-id 自定義屬性:
<button type="button" class="layui-btn layui-btn-xs btn-edit" data-id="{{$value.Id}}">編輯</button>在展示彈出層之后,根據 id 的值發起請求獲取文章分類的數據,并填充到表單中:
var id = $(this).attr('data-id') // 發起請求獲取對應分類的數據 $.ajax({method: 'GET',url: '/my/article/cates/' + id,success: function(res) {form.val('form-edit', res.data)} })1.3 更新文章分類的數據
通過代理的形式,為修改分類的表單綁定 submit 事件:
$('body').on('submit', '#form-edit', function(e) {e.preventDefault()$.ajax({method: 'POST',url: '/my/article/updatecate',data: $(this).serialize(),success: function(res) {if (res.status !== 0) {return layer.msg('更新分類數據失敗!')}layer.msg('更新分類數據成功!')layer.close(indexEdit)initArtCateList()}}) })1.4 刪除文章分類
為刪除按鈕綁定 btn-delete 類名,并添加 data-id 自定義屬性:
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs btn-delete" data-id="{{$value.Id}}">刪除</button>通過代理的形式,為刪除按鈕綁定點擊事件:
$('tbody').on('click', '.btn-delete', function() {var id = $(this).attr('data-id') // 獲取 id 的值// 提示用戶是否要刪除layer.confirm('確認刪除?', { icon: 3, title: '提示' }, function(index) {$.ajax({method: 'GET',url: '/my/article/deletecate/' + id,success: function(res) {if (res.status !== 0) {return layer.msg('刪除分類失敗!')}layer.msg('刪除分類成功!')layer.close(index)initArtCateList()}})}) })2. 文章列表
共分為3個區域,依卡片面板上的布局設計,從上到下依次是:
- 篩選區域;
- 列表區域;
- 分頁區域。
2.1 創建文章列表頁面
首先,繪制卡片區域,打開layui官網,復制卡片面板的HTML結構代碼
新建 /article/art_list.html 頁面結構如下:
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><link rel="stylesheet" href="/assets/lib/layui/css/layui.css" /><link rel="stylesheet" href="/assets/css/article/art_list.css" /></head><body><!-- 卡片區域 --><div class="layui-card"><div class="layui-card-header">文章列表</div><div class="layui-card-body"></div></div><!-- 導入第三方的 JS 插件 --><script src="/assets/lib/layui/layui.all.js"></script><script src="/assets/lib/jquery.js"></script><script src="/assets/js/baseAPI.js"></script><!-- 導入自己的 JS 腳本 --><script src="/assets/js/article/art_list.js"></script></body> </html>新建 /assets/css/article/art_list.css 樣式表如下:
html, body {margin: 0;padding: 0; }body {padding: 15px;background-color: #f2f3f5; }新建 /assets/js/article/art_list.js 腳本文件。
2.2 定義查詢參數對象q
定義一個查詢的參數對象如下:
// 定義一個查詢的參數對象,將來請求數據的時候,// 需要將請求參數對象提交到服務器var q = {pagenum: 1, // 頁碼值,默認請求第一頁的數據pagesize: 2, // 每頁顯示幾條數據,默認每頁顯示2條cate_id: '', // 文章分類的 Idstate: '' // 文章的發布狀態}2.3 請求文章列表數據并使用模板引擎渲染列表結構
API 接口如圖(注意,請求時需攜帶 4 個 參數):
2.3.1 定義獲取文章列表數據
1)定義查詢的參數對象q, 將來請求數據時,需要將請求參數對象提交到服務器。
var q = {pagenum: 1, // 默認值,默認請求第一頁的數據 pagesize: 2, // 每頁顯示幾條數據cate_id: '', // 文章分類的id,默認為空state: '' // 文章的發布狀態}2)定義一個獲取文章列表數據的方法(自定義函數)
注: 要先導入模板引擎
在layui官網找到 【頁面元素】?\Rightarrow?【表格】?\Rightarrow?【常規用法】,復制html結構到代碼編輯器中。
修改后的表格結構如下:
<!-- 列表區域 --> <table class="layui-table"><colgroup><col /><col width="150" /><col width="180" /><col width="150" /><col width="150" /></colgroup><thead><tr><th>文章標題</th><th>分類</th><th>發表時間</th><th>狀態</th><th>操作</th></tr></thead><tbody></tbody> </table>定義列表數據的模板結構:
<script type="text/html" id="tpl-table">{{each data}}<tr><td>{{$value.title}}</td><td>{{$value.cate_name}}</td><td>{{$value.pub_date|dataFormat}}</td><td>{{$value.state}}</td><td><button type="button" class="layui-btn layui-btn-xs">編輯</button><button type="button" class="layui-btn layui-btn-danger layui-btn-xs">刪除</button></td></tr>{{/each}} </script>2.4 定義美化時間格式的過濾器
通過 template.defaults.imports 定義過濾器(此屬性模板引擎獨有,需先導入模板引擎):
// 定義美化時間的過濾器template.defaults.imports.dataFormat = function(date) {const dt = new Date(date)var y = dt.getFullYear()var m = padZero(dt.getMonth() + 1)var d = padZero(dt.getDate())var hh = padZero(dt.getHours())var mm = padZero(dt.getMinutes())var ss = padZero(dt.getSeconds())return y + '-' + m + '-' + d + ' ' + hh + ':' + mm + ':' + ss}// 定義補零的函數function padZero(n) {return n > 9 ? n : '0' + n}在模板引擎中使用過濾器:
<td>{{$value.pub_date|dataFormat}}</td>2.5 繪制篩選區域的UI結構
繪制 UI 結構:
<!-- 篩選區域 --> <form class="layui-form" id="form-search"><div class="layui-form-item layui-inline"><select name="cate_id"></select></div><div class="layui-form-item layui-inline"><select name="state"><option value="">所有狀態</option><option value="已發布">已發布</option><option value="草稿">草稿</option></select></div><div class="layui-form-item layui-inline"><button class="layui-btn" lay-submit lay-filter="formDemo">篩選</button></div> </form>2.6 發起請求獲取并渲染文章分類的下拉選擇框
定義 initCate 函數請求文章分類的列表數據:
initCate()// 初始化文章分類的方法function initCate() {$.ajax({method: 'GET',url: '/my/article/cates',success: function(res) {if (res.status !== 0) {return layer.msg('獲取分類數據失敗!')}// 調用模板引擎渲染分類的可選項var htmlStr = template('tpl-cate', res)$('[name=cate_id]').html(htmlStr)// 通過 layui 重新渲染表單區域的UI結構form.render()}})}定義分類可選項的模板結構:
<script type="text/html" id="tpl-cate"><option value="">所有分類</option>{{each data}}<option value="{{$value.Id}}">{{$value.name}}</option>{{/each}} </script>2.7 實現篩選的功能
為篩選表單綁定 submit 事件:
$('#form-search').on('submit', function(e) {e.preventDefault()// 獲取表單中選中項的值var cate_id = $('[name=cate_id]').val()var state = $('[name=state]').val()// 為查詢參數對象 q 中對應的屬性賦值q.cate_id = cate_idq.state = state// 根據最新的篩選條件,重新渲染表格的數據initTable() })3. 分頁
在 layui 官方文檔中找到 【內置模塊】?\Rightarrow?【分頁】,
1)在自己 vs code 中 art_list.html 分頁區域的位置 ,添加一個 id 名為 pageBox 的 div 容器,用于存放分頁組件
2)初始化分頁區:從 layui 中導入 laypage 對象,即復制上圖上的代碼 var laypage=layui.laypage,并在 art_list.js 頭部導入此句代碼。
3)js 中調用 laypage.render 方法渲染分頁的結構。注意要傳入 4 個參數
| elem | 指向存放分頁的容器,值可以是容器ID、DOM對象。如: 1. elem: ‘id’ 注意:這里不能加 # 號 2. elem: document.getElementById(‘id’) | String/Object | - |
| count | 數據總數。一般通過服務端得到 | Number | - |
| limit | 每頁顯示的條數。laypage將會借助 count 和 limit 計算出分頁數。 | Number | 10 |
| curr | 起始頁。一般用于刷新類型的跳頁以及HASH跳頁。 | Number | 1 |
保存并刷新網頁后,效果如下:
3.1 定義渲染分頁的 renderPage 方法
定義渲染分頁的方法:
function renderPage(total) {console.log(total) }在 initTable 中調用 renderPage 方法:
function initTable() {$.ajax({method: 'GET',url: '/my/article/list',data: q,success: function(res) {if (res.status !== 0) {return layer.msg('獲取文章列表失敗!')}// 使用模板引擎渲染頁面的數據var htmlStr = template('tpl-table', res)$('tbody').html(htmlStr)// 調用渲染分頁的方法renderPage(res.total)}}) }3.2 調用 laypage.render 方法渲染分頁的基本結構
在頁面中定義分頁的區域:
<!-- 分頁區域 --> <div id="pageBox"></div>調用 laypage.render() 方法來渲染分頁的結構:
// 定義渲染分頁的方法 function renderPage(total) {// 調用 laypage.render() 方法來渲染分頁的結構laypage.render({elem: 'pageBox', // 分頁容器的 Idcount: total, // 總數據條數limit: q.pagesize, // 每頁顯示幾條數據curr: q.pagenum // 設置默認被選中的分頁}) }3.3 在jump回調函數中通過obj.curr獲取到最新的頁碼值
// 定義渲染分頁的方法 function renderPage(total) {// 調用 laypage.render() 方法來渲染分頁的結構laypage.render({elem: 'pageBox', // 分頁容器的 Idcount: total, // 總數據條數limit: q.pagesize, // 每頁顯示幾條數據curr: q.pagenum, // 設置默認被選中的分頁// 分頁發生切換的時候,觸發 jump 回調jump: function(obj) {console.log(obj.curr)// 把最新的頁碼值,賦值到 q 這個查詢參數對象中q.pagenum = obj.curr}}) }3.4 解決 jump 回調函數發生死循環的問題
分頁里面有個 jump 回調函數(包括兩個參數,obj和first),通過這個回調就可以拿到當前的頁碼值。只要點擊頁碼或者調用了 laypage.render() 方法,就會觸發 jump 回調。
jump 中,能監聽到分頁切換事件
// 定義渲染分頁的方法function renderPage(total) {// 調用 laypage.render() 方法來渲染分頁的結構laypage.render({elem: 'pageBox', // 分頁容器的 Idcount: total, // 總數據條數limit: q.pagesize, // 每頁顯示幾條數據curr: q.pagenum, // 設置默認被選中的分頁// 分頁發生切換的時候,觸發 jump 回調// 觸發 jump 回調的方式有兩種:// 1. 點擊頁碼的時候,會觸發 jump 回調// 2. 只要調用了 laypage.render() 方法,就會觸發 jump 回調jump: function(obj, first) {// 可以通過 first 的值,來判斷是通過哪種方式,觸發的 jump 回調// 如果 first 的值為 true,證明是方式2觸發的// 否則就是方式1觸發的console.log(first)console.log(obj.curr)// 把最新的頁碼值,賦值到 q 這個查詢參數對象中q.pagenum = obj.curr// 根據最新的 q 獲取對應的數據列表,并渲染表格// initTable() // 不能直接在這里調用,會形成死循環。if (!first) {initTable()}}})}3.5 自定義分頁的功能項
// 定義渲染分頁的方法 function renderPage(total) {// 調用 laypage.render() 方法來渲染分頁的結構laypage.render({elem: 'pageBox', // 分頁容器的 Idcount: total, // 總數據條數limit: q.pagesize, // 每頁顯示幾條數據curr: q.pagenum, // 設置默認被選中的分頁layout: ['count', 'limit', 'prev', 'page', 'next', 'skip'],limits: [2, 3, 5, 10],// 分頁發生切換的時候,觸發 jump 回調// 觸發 jump 回調的方式有兩種:// 1. 點擊頁碼的時候,會觸發 jump 回調// 2. 只要調用了 laypage.render() 方法,就會觸發 jump 回調jump: function(obj, first) {// 可以通過 first 的值,來判斷是通過哪種方式,觸發的 jump 回調// 如果 first 的值為 true,證明是方式2觸發的// 否則就是方式1觸發的console.log(first)console.log(obj.curr)// 把最新的頁碼值,賦值到 q 這個查詢參數對象中q.pagenum = obj.curr// 根據最新的 q 獲取對應的數據列表,并渲染表格// initTable()if (!first) {initTable()}}}) }3.6 實現切換每頁展示多少條數據的功能
// 定義渲染分頁的方法 function renderPage(total) {// 調用 laypage.render() 方法來渲染分頁的結構laypage.render({elem: 'pageBox', // 分頁容器的 Idcount: total, // 總數據條數limit: q.pagesize, // 每頁顯示幾條數據curr: q.pagenum, // 設置默認被選中的分頁layout: ['count', 'limit', 'prev', 'page', 'next', 'skip'],limits: [2, 3, 5, 10],// 分頁發生切換的時候,觸發 jump 回調// 觸發 jump 回調的方式有兩種:// 1. 點擊頁碼的時候,會觸發 jump 回調// 2. 只要調用了 laypage.render() 方法,就會觸發 jump 回調jump: function(obj, first) {// 可以通過 first 的值,來判斷是通過哪種方式,觸發的 jump 回調// 如果 first 的值為 true,證明是方式2觸發的// 否則就是方式1觸發的console.log(first)console.log(obj.curr)// 把最新的頁碼值,賦值到 q 這個查詢參數對象中q.pagenum = obj.curr// 把最新的條目數,賦值到 q 這個查詢參數對象的 pagesize 屬性中q.pagesize = obj.limit// 根據最新的 q 獲取對應的數據列表,并渲染表格// initTable()if (!first) {initTable()}}}) }4. 刪除文章
4.1 實現刪除文章的功能
為刪除按鈕綁定 btn-delete 類名和 添加data-id 自定義屬性:
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs btn-delete" data-id="{{$value.Id}}">刪除</button>通過代理的形式,為刪除按鈕綁定點擊事件處理函數:
$('tbody').on('click', '.btn-delete', function() {// 獲取到文章的 idvar id = $(this).attr('data-id') // 這句一定要放在詢問提示框之前,否則會刪除失敗!// 詢問用戶是否要刪除數據layer.confirm('確認刪除?', { icon: 3, title: '提示' }, function(index) {$.ajax({method: 'GET',url: '/my/article/delete/' + id,success: function(res) {if (res.status !== 0) {return layer.msg('刪除文章失敗!')}layer.msg('刪除文章成功!')initTable()}})layer.close(index)}) })4.2 解決刪除文章時的小 Bug
$('tbody').on('click', '.btn-delete', function() {// 獲取刪除按鈕的個數var len = $('.btn-delete').length// 獲取到文章的 idvar id = $(this).attr('data-id')// 詢問用戶是否要刪除數據layer.confirm('確認刪除?', { icon: 3, title: '提示' }, function(index) {$.ajax({method: 'GET',url: '/my/article/delete/' + id,success: function(res) {if (res.status !== 0) {return layer.msg('刪除文章失敗!')}layer.msg('刪除文章成功!')// 當數據刪除完成后,需要判斷當前這一頁中,是否還有剩余的數據// 如果沒有剩余的數據了,則讓頁碼值 -1 之后,// 再重新調用 initTable 方法// 4if (len === 1) {// 如果 len 的值等于1,證明刪除完畢之后,頁面上就沒有任何數據了// 頁碼值最小必須是 1q.pagenum = q.pagenum === 1 ? 1 : q.pagenum - 1}initTable()}})layer.close(index)}) })5. 發布文章
5.1 創建文章發布頁面的基本結構
新建 /article/art_pub.html 頁面結構如下:
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><link rel="stylesheet" href="/assets/lib/layui/css/layui.css" /><link rel="stylesheet" href="/assets/css/article/art_pub.css" /></head><body><!-- 卡片區域 --><div class="layui-card"><div class="layui-card-header">寫文章</div><div class="layui-card-body">卡片式面板面板通常用于非白色背景色的主體內<br />從而映襯出邊框投影</div></div><!-- 導入第三方的 JS 插件 --><script src="/assets/lib/layui/layui.all.js"></script><script src="/assets/lib/jquery.js"></script><script src="/assets/js/baseAPI.js"></script><!-- 導入自己的 JS --><script src="/assets/js/article/art_pub.js"></script></body> </html>新建 /assets/css/article/art_pub.css 樣式文件如下:
html, body {margin: 0;padding: 0; }body {padding: 15px;background-color: #f2f3f5; }新建 /assets/js/article/art_pub.js 腳本文件如下:
$(function() { })5.2 新建基本的表單結構
<!-- 發布文章的表單 --> <form class="layui-form"><div class="layui-form-item"><label class="layui-form-label">文章標題</label><div class="layui-input-block"><input type="text" name="title" required lay-verify="required" placeholder="請輸入標題" autocomplete="off" class="layui-input" /></div></div> </form>5.3 渲染文章類別對應的下拉選擇框結構
定義 UI 結構:
<!-- 第二行 --><div class="layui-form-item"><label class="layui-form-label">文章類別</label><div class="layui-input-block"><select name="cate_id" lay-verify="required"></select></div></div>導入 art-template:
<script src="/assets/lib/template-web.js"></script>定義模板結構:
<script type="text/html" id="tpl-cate"><option value="">請選擇文章類別</option>{{each data}}<option value="{{$value.Id}}">{{$value.name}}</option>{{/each}} </script>定義 initCate 方法:
$(function() {var layer = layui.layervar form = layui.forminitCate()// 定義加載文章分類的方法function initCate() {$.ajax({method: 'GET',url: '/my/article/cates',success: function(res) {if (res.status !== 0) {return layer.msg('初始化文章分類失敗!')}// 調用模板引擎,渲染分類的下拉菜單var htmlStr = template('tpl-cate', res)$('[name=cate_id]').html(htmlStr)// 一定要記得調用 form.render() 方法 // 由于layui不知道動態生成了分類form.render()}})} })5.4 渲染富文本編輯器
參考我的另一篇文章: 基于 Layui 的富文本編輯器和封面圖片剪裁的實現方案
與編輯器 & 圖片裁剪實現方案相關的素材文件:
網盤下載鏈接:https://pan.baidu.com/s/1k9IBXKKQfD7pf9a6NNyQkQ
提取碼:qw6o
5.5 渲染封面裁剪區域
參考 基于 Layui 的富文本編輯器和封面圖片剪裁的實現方案
5.6 渲染提交按鈕區域
<!-- 第五行 --><div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn" lay-submit>發布</button><button class="layui-btn layui-btn-primary" lay-submit>存為草稿</button></div></div>5.7 點擊選擇封面按鈕打開文件選擇框
修改 UI 結構,為 選擇封面 按鈕添加 id,并且在按鈕后面添加 文件選擇框:
<!-- 選擇封面按鈕 --> <button type="button" class="layui-btn layui-btn-danger" id="btnChooseImage">選擇封面</button> <!-- 隱藏的文件選擇框 --> <input type="file" id="coverFile" style="display: none;" accept="image/png,image/jpeg,image/gif" />為選擇封面的按鈕,綁定點擊事件處理函數:
$('#btnChooseImage').on('click', function() {$('#coverFile').click() })5.8 將選擇的圖片設置到裁剪區域中
監聽 coverFile 的 change 事件,獲取用戶選擇的文件列表:
// 監聽 coverFile 的 change 事件,獲取用戶選擇的文件列表$('#coverFile').on('change', function(e) {// 獲取到文件的列表數組var files = e.target.files// 判斷用戶是否選擇了文件if (files.length === 0) {return}// 根據文件,創建對應的 URL 地址var newImgURL = URL.createObjectURL(files[0])// 為裁剪區域重新設置圖片$image.cropper('destroy') // 銷毀舊的裁剪區域.attr('src', newImgURL) // 重新設置圖片路徑.cropper(options) // 重新初始化裁剪區域})小結 - 渲染封面裁剪區域
第一步:為文件選擇框綁定 change 事件;
第二步:在事件對象 e 中,獲取到用戶選擇的文件列表files;
第三步:判斷用戶是否選擇了文件,如果沒有選擇文件,就直接return出去,不再執行后面代碼;
第四步:用戶選擇了文件,我們需要將文件創建為對應的新的url地址;
第五步:重新為裁剪區域設置圖片。
5.9 分析發布文章的實現步驟
API 接口如圖所示:
為 存為草稿 按鈕添加 id 屬性:
<button class="layui-btn layui-btn-primary" lay-submit id="btnSave2">存為草稿</button>定義文章的發布狀態:
var art_state = '已發布'為存為草稿按鈕,綁定點擊事件處理函數:
$('#btnSave2').on('click', function() {art_state = '草稿' })5.10 基于Form表單快速創建FormData對象
為發布文章的 Form 表單添加 id 屬性:
<form class="layui-form" id="form-pub"></form>為表單綁定 submit 提交事件:
$('#form-pub').on('submit', function(e) {// 1. 阻止表單的默認提交行為e.preventDefault()// 2. 基于 form 表單,快速創建一個 FormData 對象var fd = new FormData($(this)[0])// 3. 將文章的發布狀態,追加到 fd 中fd.append('state', art_state)})快速創建 FormData 對象:new FormData($(this)[0])
代碼注釋:
在 new FormData 期間,通過把 jQuery 對象 $(this)以[0]的形式轉換為一個原生的 DOM 對象,然后把這個 DOM 對象傳遞給new FormData(),就能夠創建出一個 FormData 對象。
為了證實這個fd 對象中確實存了一些數據,我們可以用forEach將 FormData 中存儲的每一個鍵循環打印到控制臺查看,方法如下:
// 測試創建 FormData 是否成功 fd.forEach(function(v, k) { // 用v 接收FormData中存的值,k 接收鍵console.log(k, v); })提交事件觸發后,控制臺打印如下:
5.11 將裁剪后的封面追加到FormData對象中
這里仍然參照文章 基于 Layui 的富文本編輯器和封面圖片剪裁的實現方案 一文中 “4. 將裁剪后的圖片,輸出為文件” 的代碼。
代碼如圖:
只要調用了toBlob,就能將裁剪之后的圖片輸出為文件了,.toBlob(function(blob)中的blob就是圖片文件。再將 blob存到 FormData(也就是fd) 里。
5.12 發起Ajax請求實現發布文章的功能
定義一個發布文章的方法:
function publishArticle(fd) {$.ajax({method: 'POST',url: '/my/article/add',data: fd,// 注意:如果向服務器提交的是 FormData 格式的數據,// 必須添加以下兩個配置項contentType: false,processData: false,success: function(res) {if (res.status !== 0) {return layer.msg('發布文章失敗!')}layer.msg('發布文章成功!')// 發布文章成功后,跳轉到文章列表頁面location.href = '/article/art_list.html'}}) }注意:
如果向服務器提交的是 FormData 格式的數據,必須添加以下兩個配置項:
contentType: false,
processData: false,
否則,請求就會失敗。
把裁剪的圖片追加到 FormData 對象中之后,調用 publishArticle 方法:
// 為表單綁定 submit 提交事件 $('#form-pub').on('submit', function(e) {// 1. 阻止表單的默認提交行為e.preventDefault()// 2. 基于 form 表單,快速創建一個 FormData 對象var fd = new FormData($(this)[0])// 3. 將文章的發布狀態,存到 fd 中fd.append('state', art_state)// 4. 將封面裁剪過后的圖片,輸出為一個文件對象$image.cropper('getCroppedCanvas', {// 創建一個 Canvas 畫布width: 400,height: 280}).toBlob(function(blob) {// 將 Canvas 畫布上的內容,轉化為文件對象// 得到文件對象后,進行后續的操作// 5. 將文件對象,存儲到 fd 中fd.append('cover_img', blob)// 6. 發起 ajax 數據請求publishArticle(fd)}) })6. 編輯文章
6.1 創建文章編輯頁面基本結構
6.2 給編輯按鈕添加點擊事件
將/article/art.list.html文件打開,給模板引擎中的“編輯”按鈕添加類名btn_edit、自定義屬性data-index和data-id
6.3 新建基本的表單結構
在上一步創建的html結構的卡片區域中,將類名為 layui-card-body 的 div 盒子內的文字替換為如下表單(在Layui官方文檔中復制輸入框html結構),并為form表單添加id="formAddArticle"
<!-- 修改文章的表單 --> <form class="layui-form" lay-filter="addArticle" id="formAddArticle" enctype="multipart/form-data"><!-- 文章標題 --><div class="layui-form-item"><label class="layui-form-label">文章標題</label><div class="layui-input-block"><input type="text" name="title" required lay-verify="required" placeholder="請輸入標題" autocomplete="off" class="layui-input" /></div></div> </form>6.4 文章類別對應的下拉選擇框結構
定義 UI 結構:
<!-- 文章類別 --><div class="layui-form-item"><label class="layui-form-label">文章類別</label><div class="layui-input-block" id="art_cate"></div></div>導入 art-template:
在html文件底部,在導入的 layui.js 后面導入template 模板引擎腳本文件。
定義文章分類列表模板結構:
給模板結構添加 id屬性:id="selectArtCates",方便后續渲染。
定義 renderArticleCates 方法,渲染文章分類列表:
$(function() {// 獲取需要的 layui 對象var form = layui.form// 1. 渲染文章分類列表renderArticleCates()function renderArticleCates() {$.get('/my/article/cates', function(res) {if (res.status !== 0) {return layer.msg('獲取文章分類列表失敗!')}var htmlStr = template('selectArtCates', res)$('#art_cate').html(htmlStr)// 一定要記得調用 form.render() 方法 // 由于layui不知道動態生成了分類form.render()getArticleById()})}6.5 渲染富文本編輯器
參考 基于 Layui 的富文本編輯器和封面圖片剪裁的實現方案 完成。
6.6 封面裁剪區域結構
6.7 提交按鈕區域
<!-- 按鈕區域 --> <div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn" lay-submit id="btnPublish">發布</button><button class="layui-btn layui-btn-primary" lay-submit id="btnSave">存為草稿</button></div> </div>給表單添加隱藏域,用于獲取用戶選擇的文章的Id。
<input type="hidden" name="Id" />6.8 通過 URLSearchParams 對象,獲取 URL 傳遞的參數
// 通過 URLSearchParams 對象,獲取 URL 傳遞過來的參數var params = new URLSearchParams(location.search)var artId = params.get('id')// 文章的發布狀態var pubState = ''解釋:
首先聲明變量params,并通過URLSearchParams對象賦值,再通過params.get()的方式輸入鍵、拿到值(這里賦值給artId),就很簡單地 拿到了前一個頁面(文章列表頁點擊“編輯”時)傳過來的文章id值。
Location對象提供以下屬性。
| Location.href | 整個 URL |
| Location.protocol | 當前 URL 的協議,包括冒號(:) |
| Location.host | 主機,包括冒號(:)和端口(默認的80端口和443端口會省略)。 |
| Location.hostname | 主機名,不包括端口。 |
| Location.port | 端口號。 |
| Location.pathname | URL 的路徑部分,從根路徑/開始。 |
| Location.search | 查詢字符串部分,從問號?開始。 |
| Location.hash | 片段字符串部分,從#開始。 |
| Location.username | 域名前面的用戶名。 |
| Location.password | 域名前面的密碼。 |
| Location.origin | URL 的協議、主機名和端口。 |
更多關于URLSearchParams(location.search),可參考MDN文檔 和后面這篇簡書文章 《JS Location對象,URL對象,URLSearchParams對象》
6.9 發起請求獲取文章詳情
根據文章的 Id,獲取文章的詳情,并初始化表單的數據內容:
function getArticleById() {// 發起請求,獲取文章詳情$.get('/my/article/' + artId, function(res) {// 獲取數據失敗if (res.status !== 0) {return layer.msg('獲取文章失敗!')}// 獲取數據成功var art = res.data// 為 form 表單賦初始值form.val('addArticle', {Id: art.Id,title: art.title,cate_id: art.cate_id,content: art.content})// 手動初始化富文本編輯器initEditor() // cropper插件自帶方法,導入插件即可使用// 初始化圖片裁剪器var $image = $('#image')// 設置圖片路徑$image.attr('src', 'http://ajax.frontend.itheima.net' + art.cover_img)// 裁剪選項var cropperOption = {aspectRatio: 400 / 280,preview: '.img-preview',// 初始化圖片裁剪框的大小autoCropArea: 1}// 初始化裁剪區域$image.cropper(cropperOption)})}form.val:攜帶有值就給表單賦值,未攜帶值則獲取表單中的值。
語法:form.val('filter', object);
用于給指定表單集合的元素賦值和取值。如果 object 參數存在,則為賦值;如果 object 參數不存在,則為取值。其中「取值」功能為 layui 2.2.5 開始新增
attr() 方法:設置被選元素(這里是image標簽)的屬性和值。詳情參閱 W3School文檔。
裁剪選項 var cropperOption={},參見 基于Layui 的富文本編輯器與封面的實現方案
6.10 選擇文章封面
// 選擇封面$('#btnChooseCoverImage').on('click', function(e) e.preventDefault() // 阻止默認提交行為$('#fileCover').click()})6.11 監聽文件(文章封面圖片)選擇框的 change 事件
$('#fileCover').on('change', function(e) {var files = e.target.files// 沒有選擇文件if (files.length === 0) {return}// 重新為裁剪區域設置圖片$('#image').cropper('destroy').attr('src', URL.createObjectURL(files[0])).cropper({aspectRatio: 400 / 280,preview: '.img-preview'}) })6.12 設置文章的發布狀態
$('#btnPublish').on('click', function() {pubState = '已發布' }) $('#btnSave').on('click', function() {pubState = '草稿' })6.13 發布文章
綁定【發布】按鈕的submit提交事件,同樣的需參照 基于Layui 的富文本編輯器與封面的實現方案,完成后的代碼如下:
$('#formAddArticle').on('submit', function(e) {e.preventDefault() // 阻止默認提交事件// 創建一個 Canvas 畫布$('#image').cropper('getCroppedCanvas', {width: 400,height: 280})// 將 Canvas 畫布上的內容,轉化為文件對象.toBlob(function(blob) { // 5.1 組織參數對象 FormDatavar fd = new FormData($('#formAddArticle')[0])// 5.2 添加封面fd.append('cover_img', blob)// 5.3 添加文章的發表狀態fd.append('state', pubState)// 5.4 發起請求$.ajax({method: 'POST',url: '/my/article/edit',data: fd,contentType: false,processData: false,success: function(res) {if (res.status !== 0) {return layer.msg('編輯文章失敗!')}location.href = '/article/art_list.html'}})}) })toBlob()方法的語法:canvas.toBlob(callback, type, encoderOptions);
用以展示canvas上的圖片;這個圖片文件可以被緩存或保存到本地,由用戶代理端自行決定。如不特別指明,圖片的類型默認為 image/png,分辨率為 96dpi。
第三個參數用于針對image/jpeg格式的圖片進行輸出圖片的質量設置(值在0與1之間)。
更多詳情請參閱 MDN文檔
附< 文件在線轉 Base64 地址>: https://www.css-js.com/tools/base64.html
至此,文章編輯功能的開發已算完成。
6.14 完整html結構
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="/assets/lib/layui/css/layui.css"><!-- 富文本樣式 --><link rel="stylesheet" href="/assets/lib/cropper/cropper.css" /><link rel="stylesheet" href="/assets/css/article/art_edit.css"> </head><body><div class="layui-card"><div class="layui-card-header">修改文章</div><div class="layui-card-body"><!-- 修改文章的表單 --><form class="layui-form" lay-filter="addArticle" id="formAddArticle" enctype="multipart/form-data"><!-- 文章標題 --><div class="layui-form-item"><label class="layui-form-label">文章標題</label><div class="layui-input-block"><input type="text" name="title" required lay-verify="required" placeholder="請輸入標題" autocomplete="off" class="layui-input" /></div></div><!-- 文章類別 --><div class="layui-form-item"><label class="layui-form-label">文章類別</label><div class="layui-input-block" id="art_cate"></div></div><!-- 文章內容 --><div class="layui-form-item"><label class="layui-form-label">文章內容</label><div class="layui-input-block" style="height: 400px;"><textarea name="content" id="content"></textarea></div></div><!-- 文章封面 --><div class="layui-form-item"><label class="layui-form-label">文章封面</label><div class="layui-input-block cropper-container"><!-- 文件選擇框 --><input type="file" id="fileCover" accept="image/jpeg,image/png,image/gif,image/bmp" style="display: none;" /><div class="cropper-box"><img id="image" src=""></div><div class="right-box"><div class="img-preview"></div><button class="layui-btn layui-btn-danger" id="btnChooseCoverImage">選擇封面</button></div></div></div><!-- 按鈕區域 --><div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn" lay-submit id="btnPublish">發布</button><button class="layui-btn layui-btn-primary" lay-submit id="btnSave">存為草稿</button></div></div><input type="hidden" name="Id" /></form></div></div><!-- 文章分類列表的模板 --><script type="text/html" id="selectArtCates"><select name="cate_id" lay-verify="required"><option value="">請選擇文章類別</option>{{each data}}<option value="{{$value.Id}}">{{$value.name}}</option>{{/each}}</select></script><script src="/assets/lib/layui/layui.all.js"></script><script src="/assets/lib/template-web.js"></script><script src="/assets/lib/jquery.js"></script><script src="/assets/js/baseAPI.js"></script><!-- 富文本編輯器 --><script src="../../assets/lib/tinymce/tinymce.min.js"></script><script src="../../assets/lib/tinymce/tinymce_setup.js"></script><!-- 圖片裁剪 --><script src="../../assets/lib/cropper/Cropper.js"></script><script src="../../assets/lib/cropper/jquery-cropper.js"></script><script src="/assets/js/article/art_edit.js"></script> </body></html>6.15 完整css 樣式代碼
html,body {margin: 0;padding: 0; }body {padding: 15px;background-color: #f2f3f5; }.cropper-box {width: 400px;height: 280px; }#image {max-width: 400px;/* 這個設置很重要! */max-height: 280px; }.img-preview {width: 200px;height: 140px;overflow: hidden;margin-bottom: 20px; }.cropper-container {display: flex; }.cropper-box {margin-right: 15px; }.right-box {text-align: center; }6.16 編輯功能完整 JS 代碼
$(function() {// 通過 URLSearchParams 對象,獲取 前一個頁面URL 傳遞過來的參數var params = new URLSearchParams(location.search)var artId = params.get('id')// 文章的發布狀態var pubState = ''// 獲取需要的 layui 對象var form = layui.form// 1. 渲染文章分類列表renderArticleCates()function renderArticleCates() {$.get('/my/article/cates', function(res) {if (res.status !== 0) {return layer.msg('獲取文章分類列表失敗!')}var htmlStr = template('selectArtCates', res)$('#art_cate').html(htmlStr)form.render()getArticleById()})}// 2. 根據文章的 Id,獲取文章的詳情,并初始化表單的數據內容function getArticleById() {// 發起請求,獲取文章詳情$.get('/my/article/' + artId, function(res) {// 獲取數據失敗if (res.status !== 0) {return layer.msg('獲取文章失敗!')}// 獲取數據成功var art = res.data// 為 form 表單賦初始值form.val('addArticle', {Id: art.Id,title: art.title,cate_id: art.cate_id,content: art.content})// 手動初始化富文本編輯器initEditor()// 初始化圖片裁剪器var $image = $('#image')$image.attr('src', 'http://ajax.frontend.itheima.net' + art.cover_img)// $image.attr('src', 'http://www.liulongbin.top:3007' + art.cover_img)// 裁剪選項var cropperOption = {aspectRatio: 400 / 280,preview: '.img-preview',// 初始化圖片裁剪框的大小autoCropArea: 1}// 初始化裁剪區域$image.cropper(cropperOption)})}// 3. 選擇封面$('#btnChooseCoverImage').on('click', function(e) {e.preventDefault()$('#fileCover').click()})// 4. 監聽文件選擇框的 change 事件$('#fileCover').on('change', function(e) {var files = e.target.files// 沒有選擇文件if (files.length === 0) {return}// 重新為裁剪區域設置圖片$('#image').cropper('destroy').attr('src', URL.createObjectURL(files[0])).cropper({aspectRatio: 400 / 280,preview: '.img-preview'})})// 設置文章的發布狀態$('#btnPublish').on('click', function() {pubState = '已發布'})$('#btnSave').on('click', function() {pubState = '草稿'})// 5. 發布文章$('#formAddArticle').on('submit', function(e) {e.preventDefault()$('#image').cropper('getCroppedCanvas', {width: 400,height: 280}).toBlob(function(blob) {// 5.1 組織參數對象var fd = new FormData($('#formAddArticle')[0])// 5.2 添加封面fd.append('cover_img', blob)// 5.3 添加文章的發表狀態fd.append('state', pubState)// 5.4 發起請求$.ajax({method: 'POST',url: '/my/article/edit',data: fd,contentType: false,processData: false,success: function(res) {if (res.status !== 0) {return layer.msg('編輯文章失敗!')}location.href = '/article/art_list.html'}})})}) })7. 將開發完成的項目代碼推送到GitHub
1、檢查當前所在分支:
git branch2、本地提交
git add .3、檢查所有文件狀態
git status4、運行 git commit -m
git commit -m "完成文章管理相關功能的開發"5、將本地article分支提交到 Github 存儲
git push -u origin article6、將本地article分支里最新的代碼合并到master主分支
1) 切換到 master 主分支
2)合并article里最新的代碼
git merge article7、將本地 master最新代碼推送到Github中
git push 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的大事件后台管理系统开发实战(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue-router 动态路由匹配
- 下一篇: vue解决字符串模板@click无效的问