如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
了解一個技術,首先要了解這個技術產生的背景及解決的問題,而不應該只是單純的知道該怎么用。之前的狀態可能就是只是為了了解而了解,并不知道實際產生的原因及帶來的好處,所以今天就來總結一下。
來看百度百科的定義
模塊化程序設計是指在進行程序設計時將一個大程序按照功能劃分為若干小程序模塊,每個小程序模塊完成一個確定的功能,并在這些模塊之間建立必要的聯系,通過模塊的互相協作完成整個功能的程序設計方法。
比如 java 的 import,C# 的 using。我的理解是通過模塊化編程,可以將不同的功能獨立出來,修改某個功能時不會對其他功能產生影響。
來看下面一個例子
// A.js function sayWord(type){ if(type === 1){
console.log("hello");
}else if(type === 2){
console.log("world");
}
} // B.js function Hello(){ sayWord(1);
} // C.js Hello()
假設上面三個文件,B.js 引用了 A.js 里面的內容,C.js 又引用了 B.js 里面的內容,如果編寫 C.js 的人只知道引用了 B.js,那他就不會引用 A.js 就會導致程序出錯,而且文件的引用順序也不能出錯。給整體代碼的調試修改帶來不便。
還有個問題,上述代碼暴露了兩個全局變量,容易造成全局變量的污染
AMD 即 Asynchronous Module Definition(異步模塊定義)。采取異步加載的方式加載模塊,模塊的加載不會影響它后面的語句執行。而且只有用到的時候才會去加載相關文件,屬于瀏覽器端的標準
假設下面這種情況
// util.js define(function(){ return {
getFormatDate:function(date,type){ if(type === 1){ return '2018-08-9' } if(type === 2){ return '2018 年 8 月 9 日' }
}
}
}) // a-util.js define(['./util.js'],function(util){ return {
aGetFormatDate:function(date){ return util.getFormatDate(date,2)
}
}
}) // a.js define(['./a-util.js'],function(aUtil){ return {
printDate:function(date){ console.log(aUtil.aGetFormatDate(date))
}
}
}) // main.js require(['./a.js'],function(a){ var date = new Date()
a.printDate(date)
})
console.log(1); // 使用 // <script src = "/require.min.js" data-main="./main.js"></script>
頁面上先打印 1
,然后才會打印 2018 年 8 月 9 日
。因此 AMD 的加載并不會影響后續的語句執行。
如果不是異步加載會出現什么情況呢
var a = require('a');
console.log(1)
后面的語句需要等待 a 加載完成才能執行,如果加載時間過長,整個程序都會卡在這。因此,瀏覽器不能同步加載資源,這也是 AMD 的產生背景。
AMD 是在瀏覽器端實現模塊化開發的規范。由于該規范不是 JavaScript 原始支持的,使用 AMD 規范進行開發的時候需要引入第三方的庫函數,也就是 RequireJS。
RequireJS 主要解決的問題
下面來看看如何使用 require.js
要想使用 require.js,首先要 define
// ? 代表該參數可選 define(id?, dependencies?, factory);
factory:為模塊初始化要執行的函數或對象。如果為函數,它應該只被執行一次。如果是對象,此對象應該為模塊的輸出值。
具體的規范說明可以參考 AMD (中文版)
舉個例子,創建一個名為 “alpha” 的模塊,使用了 require,exports,和名為 “beta” 的模塊:
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //Or: return require("beta").verb();
}
});
一個返回對象的匿名模塊:
define(["alpha"], function (alpha) { return {
verb: function(){ return alpha.verb() + 2;
}
};
});
一個沒有依賴性的模塊可以直接定義對象:
define({
add: function(x, y){ return x + y;
}
});
如何使用
AMD 采用 require 語句加載模塊
require([module],callback);
例如
require(['./a.js'],function(a){ var date = new Date()
a.printDate(date)
})
具體的使用方法如下
// util.js define(function(){ return {
getFormatDate:function(date,type){ if(type === 1){ return '2018-08-09' } if(type === 2){ return '2018 年 8 月 9 日' }
}
}
}) // a-util.js define(['./util.js'],function(util){ return {
aGetFormatDate:function(date){ return util.getFormatDate(date,2)
}
}
}) // a.js define(['./a-util.js'],function(aUtil){ return {
printDate:function(date){ console.log(aUtil.aGetFormatDate(date))
}
}
}) // main.js require(['./a.js'],function(a){ var date = new Date()
a.printDate(date)
}) // 使用 // <script src = "/require.min.js" data-main="./main.js"></script>
假設這里有 4 個文件,util.js,a-util.js 引用了 util.js,a.js 引用了 a-util.js,main.js 引用了 a.js。
其中,data-main 屬性的作用是加載網頁程序的主模塊。
上例演示了一個主模塊最簡單的寫法,默認情況下,require.js 假設依賴和主模塊在同一個目錄。
使用 require.config()
方法可以對模塊的加載行為進行自定義。require.config()
就寫在主模塊(main.js)的頭部,參數是一個對象,這個對象的 paths 屬性指定各個模塊的加載路徑
require.config({
paths:{ "a":"src/a.js", "b":"src/b.js" }
})
還有一種方法是改變基礎目錄(baseUrl)
require.config({
baseUrl: "src",
paths: { "a": "a.js", "b": "b.js",
}
});
commonJS 是 nodejs 的模塊化規范,現在被大量用在前端,由于構建工具的高度自動化,使得使用 npm 的成本非常低。commonJS 不會異步加載 JS,而是同步一次性加載出來
在 commonJS 中,有一個全局性的方法 require(),用于加載模塊,例如
const util = require('util');
然后,就可以調用 util 提供的方法了
const util = require('util'); var date = new date();
util.getFormatDate(date,1);
commonJS 對于模塊的定義分三種,模塊定義(exports),模塊引用(require)和模塊標示(module)
exports() 對象用于導出當前模塊的變量或方法,唯一的導出口。require() 用來引入外部模塊。module 對象代表模塊本身。
舉個栗子
// util.js module.exports = {
getFormatDate:function(date, type){ if(type === 1){ return '2017-06-15' } if(type === 2){ return '2017 年 6 月 15 日' }
}
} // a-util.js const util = require('util.js')
module.exports = {
aGetFormatDate:function(date){ return util.getFormatDate(date,2)
}
}
或者下面這種方式
// foobar.js // 定義行為 function foobar(){ this.foo = function(){ console.log('Hello foo');
} this.bar = function(){ console.log('Hello bar');
}
} // 把 foobar 暴露給其它模塊 exports.foobar = foobar; // main.js //使用文件與模塊文件在同一目錄 var foobar = require('./foobar').foobar,
test = new foobar();
test.bar(); // 'Hello bar'
ES6 模塊的設計思想是盡量靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量,而 CommonJS 和 AMD 模塊都只能在運行時確定這些關系。如 CommonJS 加載方式為 “運行時加載”,ES6 的加載方式為 “編譯時加載” 或者靜態加載,即 ES6 可以在編譯時就完成模塊加載,效率比 CommonJS 模塊的加載方式高。
ES6 模塊自動采用嚴格模式,不管有沒有在模塊頭部加上 “use strict”。
ES6 export 語句輸出的接口與其對應的值是動態綁定關系,即通過該接口可以取到模塊內部實時的值。而 CommonJS 模塊輸出的是值的緩存,不存在動態更新。
ES6 與 CommonJS 模塊的差異
第二個差異是因為 CommonJS 加載的是一個對象(即 Module.exports 屬性),該對象只有在腳本運行結束時才會生成,而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。
CommonJS 采用了服務器優先的策略,使用同步方式加載模塊,而 AMD 采用異步加載的方式。所以如果需要使用異步加載 js 的話建議使用 AMD,而當項目使用了 npm 的情況下建議使用 CommonJS。
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
在小程序里面有兩個地方獲取用戶的openid。
一個是wx.login(OBJECT),第二個是wx.getUserInfo(OBJECT)。
這里我使用的是第一種wx.login(OBJECT)。
wx.login({
success: function(res) { if (res.code) { // 第一步: 獲取code //發起網絡請求 wx.request({
url: '后臺接口', // 獲取openid data: {
code: res.code
}
})
} else {
console.log('獲取用戶登錄態失??!' + res.errMsg)
}
}
});
后端的實現就是后端調用這個接口:https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code。
/*
* 根據code獲取微信用戶的openid
*/ router.get('/api/getWxCode', function(req, res, next) { var param = req.query || req.params; var code = param.code; var urlStr = 'https://api.weixin.qq.com/sns/jscode2session?appid=' + wxConfig.AppID + '&secret=' + wxConfig.Secret + '&js_code=' + code + '&grant_type=authorization_code';
request(urlStr, function (error, response, body) { if (!error && response.statusCode == 200) { var jsBody = JSON.parse(body);
jsBody.status = 100;
jsBody.msg = '操作成功';
res.end(JSON.stringify(jsBody));
}
})
});
/**
* 生命周期函數--監聽頁面加載
*/ onLoad: function (options) { var self = this;
wx.login({
success: function (res) { if (res.code) { //發起網絡請求 wx.request({
url: 'https://www.hgdqdev.cn/api/getWxCode',
data: {
code: res.code
},
success: function(res){ if(res.data.status == 100){ self.setData({
openid: res.data.openid
})
}
},
fail: function(){ }
})
} else {
console.log('獲取用戶登錄態失?。? + res.errMsg)
}
}
});
},
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
我也是剛學自動化,在自學過程中發現瀏覽器中有一些彈窗元素,無法定位。經過自己的摸索,有一些心得,寫下來供日后自己回顧。
首先要確定彈窗的類型:
(1)div彈窗
(2)新標簽頁彈窗
(3)alert彈窗
一,div彈窗
div彈窗是瀏覽器中比較好定位的彈窗,定位的方法與普通的元素一樣。不過這里會有一個坑,明明可以找到這個按鈕,但是就是定位不到。這個就是因為當前有div彈窗彈出的時候,需要設置一下等待時間,等頁面元素加載完畢,再去做其他操作。
這里用百度登陸為例子:
from selenium import webdriver import time def login_baidu(url,username,password): driver.get(url)
driver.find_element_by_xpath('//*[@id="u1"]/a[7]').click()
time.sleep(2)
driver.find_element_by_xpath('//*[@id="TANGRAM__PSP_10__footerULoginBtn"]').click()
time.sleep(2) # 彈窗出現后,使頁面等待2S login_username = driver.find_element_by_xpath('//*[@id="TANGRAM__PSP_10__userName"]')
login_username.click()
login_username.send_keys(username)
login_passwork = driver.find_element_by_xpath('//*[@id="TANGRAM__PSP_10__password"]')
login_passwork.click()
login_passwork.send_keys(password)
driver.find_element_by_xpath('//*[@id="TANGRAM__PSP_10__submit"]').click() # 登陸按鈕 if __name__ == "__main__":
driver = webdriver.Firefox()
username = r'xxx@qq.com' password = r'xxxx' url = r'https://www.baidu.com' login_baidu(url,username,password)
二,新標簽頁彈窗
新標簽頁彈窗,則需要進行窗口的切換。此處第一個窗口打開百度首頁,在打開一個新窗口打開京東首頁,在兩個窗口之間進行切換。切換到不同的窗口之后,就可以用常規的方法進行元素的定位。
from selenium import webdriver import time def open_window1(): driver.get("https://www.baidu.com")
time.sleep(2) def open_window2(): # 用JS的方法打開新窗口,模擬新標簽頁彈窗 js = "window.open('https://www.jd.com')" driver.execute_script(js)
time.sleep(2) def Switch_Window(): handles = driver.window_handles
print("打印當前已打開的窗口:"+str(handles)) while(5): # 在兩個窗口之間做五次切換動作 driver.switch_to.window(handles[0])
time.sleep(5)
driver.switch_to.window(handles[1])
time.sleep(5) if __name__ == "__main__" :
driver = webdriver.Firefox()
open_window1()
open_window2()
Switch_Window()
handles = driver.window_handles # 獲取當前打開的所有窗口的句柄
driver.switch_to.window(handles[N]) # 切換到其中一個窗口
其中,獲取的句柄下標從0開始,即第一個窗口為[0]、第二個窗口為[1],如此類推。使用switch_to.window方法切換到新標簽頁后就可以做其他操作了。
三、alert彈窗
該類型的彈窗暫沒有合適的項目進行練習,待后續完善
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
選擇器
學習目的 熟練使用 css 選擇器
css1 中的選擇器
E #myid id選擇器
E .warning 類選擇器
E F 包含選擇器
E:link 定義超鏈接未被訪問
E:visited 定義超鏈接已經被訪問
E:actice 匹配被激活的元素
E:hover 鼠標經過的元素
E:focus 獲取焦點
E::first-line 元素第一行
E::first-letter 元素第一個字符
css2
* 通配選擇文檔中所有元素
E[foo] 包含foo屬性的元素
E[foo="bar"] 包含屬性foo值為bar的元素
CSS3 中的選擇器可替代 了解即可
E[foo~="bar"] 含有屬性foo值包括bar的元素例如 <a foo="bar bar1 bar2">link</a>
E[foo|="en"] 屬性值是一個“-”分割的 比如 en-us
E:first-child 父元素的第一個子元素
E:lang(fr) 匹配屬性,元素顯示內容為語言為 fr
E::before 在元素前面插入內容
E::after 在元素后面插入內容
E>F 子包含
E+E 相鄰兄弟選擇器 后面的兄弟
css3
E[foo^="bar"] 屬性foo的值開頭是bar
E[foo$="bar"] 屬性foo的值得結尾是bar
E[foo*="bar"] 屬性foo的值包含bar <a foo="abc_bar_as">link</a>
結構類選擇器
E:root 屬性文檔所在的根元素
E:nth-child(n) E元素第n個位置的子元素 n可以是 (1,2,3) 關鍵字(odd,even) 公式(2n,2n+3) 起始值為1
E:nth-last-child(n) 與上面的使用方法一樣 倒數的第N個位置的子元素
E:nth-of-type(n) 匹配父元素中與E相同的元素中的第n個元素
E:nth-last-of-type(n) 匹配父元素中與E相同的倒數第n個元素
E:last-child 選擇位于其父元素的最后一個位置,且匹配E的子元素
E:first-of-type 選擇在其父元素中匹配E的第一個同類型的子元素
E:last-of-type 選擇在其父元素中匹配E的最后一個同類型的子元素
E:only-child 選擇其父元素只包含一個子元素,且該子元素匹配E
E:only-of-type 選擇其父元素只包含一個同類型的子元素,且該子元素匹配E
E:empty 選擇匹配E的元素,且該元素不包含子節點,文本也是節點
E~F 通用兄弟選擇器
E:not(s) 否定偽類選擇器
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
前言
對比網上的例子
<a href="#" id="username" data-type="text" data-pk="1">awesome</a> <script> $(function(){ $('#username').editable({
url: '/post',
title: 'Enter username' });
}); </script>
$('#db_dependences').bootstrapTable({
method:'POST',
dataType:'json',
contentType: "application/x-www-form-urlencoded",
cache: false,
striped: true, //是否顯示行間隔色 sidePagination: "client", //分頁方式:client客戶端分頁,server服務端分頁(*) showColumns:true,
pagination:true,
minimumCountColumns:2,
pageNumber:1, //初始化加載第一頁,默認第一頁 pageSize: 10, //每頁的記錄行數(*) pageList: [10, 15, 20, 25], //可供選擇的每頁的行數(*) uniqueId: "id", //每一行的唯一標識,一般為主鍵列 showExport: true,
exportDataType: 'all',
exportTypes:[ 'csv', 'txt', 'sql', 'doc', 'excel', 'xlsx', 'pdf'], //導出文件類型 onEditableSave: function (field, row, oldValue, $el) { $.ajax({
success: function (data, status) { if (status == "success") {
alert("編輯成功");
}
},
error: function () { alert("Error");
},
complete: function () { }
});
},
data: [{
id: 1,
name: '張三',
sex: '男',
time: '2017-08-09' }, {
id: 2,
name: '王五',
sex: '女',
time: '2017-08-09' }, {
id: 3,
name: '李四',
sex: '男',
time: '2017-08-09' }, {
id: 4,
name: '楊朝來',
sex: '男',
time: '2017-08-09' }, {
id: 5,
name: '蔣平',
sex: '男',
time: '2017-08-09' }, {
id: 6,
name: '唐燦華',
sex: '男',
time: '2017-08-09' }],
columns: [{
field: 'id',
title: '序號' }, {
field: 'name',
title: '姓名',
editable: {
type: 'text',
validate: function (value) { if ($.trim(value) == '') { return '姓名不能為空!';
}
}
}
}, {
field: 'sex',
title: '性別',
editable: {
type: 'select',
pk: 1,
source: [
{value: 1, text: '男'},
{value: 2, text: '女'},
]
}
}, {
field: 'time',
title: '時間',
editable: {
type: 'date',
format: 'yyyy-mm-dd',
viewformat: 'yyyy-mm-dd',
datepicker: {
weekStart: 1 }
}
}]
});
結果圖如下:
由于開源,很快就找到原因,由于formatter我們沒有寫這個function導致調用的默認的formatter,默認的沒有把表格的值傳入html中,bootstrap-table-editable.js源碼如下,初始定義_dont_edit_formatter為false,我們沒有實現noeditFormatter的function就會執行第二個if語句,其中的標簽中沒有對內容賦值,導致最后顯示結果為它默認的Empty:
column.formatter = function(value, row, index) { var result = column._formatter ? column._formatter(value, row, index) : value;
$.each(column, processDataOptions);
$.each(editableOptions, function(key, value) {
editableDataMarkup.push(' ' + key + '="' + value + '"');
}); var _dont_edit_formatter = false; if (column.editable.hasOwnProperty('noeditFormatter')) {
_dont_edit_formatter = column.editable.noeditFormatter(value, row, index);
} if (_dont_edit_formatter === false) { return ['<a href="javascript:void(0)"', ' data-name="' + column.field + '"', ' data-pk="' + row[that.options.idField] + '"', ' data-value="' + result + '"',
editableDataMarkup.join(''), '>' + '</a>' ].join('');
} else { return _dont_edit_formatter;
}
};
由于要實現多樣式,則把上面的代碼改變,并在使用的時候實現noeditFormatter:function(value){…}就是了。將上面的代碼改為如下(此為我自己改的,你可以根據自己的需要做修改):
column.formatter = function(value, row, index) { var result = column._formatter ? column._formatter(value, row, index) : value;
$.each(column, processDataOptions);
$.each(editableOptions, function(key, value) {
editableDataMarkup.push(' ' + key + '="' + value + '"');
}); var _dont_edit_formatter = false; if (column.editable.hasOwnProperty('noeditFormatter')) { var process = column.editable.noeditFormatter(value, row, index); if(!process.hasOwnProperty('class')){
process.class = '';
} if(!process.hasOwnProperty('style')){
process.style = '';
}
_dont_edit_formatter = ['<a href="javascript:void(0)"', ' data-name="'+process.filed+'"', ' data-pk="1"', ' data-value="' + process.value + '"', ' class="'+process.class+'" style="'+process.style+'"', '>' + process.value + '</a>' ].join('');
} if (_dont_edit_formatter === false) { return ['<a href="javascript:void(0)"', ' data-name="' + column.field + '"', ' data-pk="' + row[that.options.idField] + '"', ' data-value="' + result + '"',
editableDataMarkup.join(''), '>' + value + '</a>' ].join('');
} else { return _dont_edit_formatter;
}
};
結果如下:
然后是bootstrap table的使用js文件,在其中實現noeditFormatter函數。返回的result必須包含filed和value,class和style可以不需要,class可以額外用其它插件之類,比如badge,style是增加樣式(背景,顏色,字體等)。
$('#db_dependences').bootstrapTable({
method:'POST',
dataType:'json',
contentType: "application/x-www-form-urlencoded",
cache: false,
striped: true, //是否顯示行間隔色 sidePagination: "client", //分頁方式:client客戶端分頁,server服務端分頁(*) showColumns:true,
pagination:true,
minimumCountColumns:2,
pageNumber:1, //初始化加載第一頁,默認第一頁 pageSize: 10, //每頁的記錄行數(*) pageList: [10, 15, 20, 25], //可供選擇的每頁的行數(*) uniqueId: "id", //每一行的唯一標識,一般為主鍵列 showExport: true,
exportDataType: 'all',
exportTypes:[ 'csv', 'txt', 'sql', 'doc', 'excel', 'xlsx', 'pdf'], //導出文件類型 onEditableSave: function (field, row, oldValue, $el) { $.ajax({
success: function (data, status) { if (status == "success") {
alert("編輯成功");
}
},
error: function () { alert("Error");
},
complete: function () { }
});
}, // onEditableHidden: function(field, row, $el, reason) { // 當編輯狀態被隱藏時觸發 // if(reason === 'save') { // var $td = $el.closest('tr').children(); // // $td.eq(-1).html((row.price*row.number).toFixed(2)); // // $el.closest('tr').next().find('.editable').editable('show'); //編輯狀態向下一行移動 // } else if(reason === 'nochange') { // $el.closest('tr').next().find('.editable').editable('show'); // } // }, data: [{
id: 1,
name: '張三',
sex: '男',
time: '2017-08-09' }, {
id: 2,
name: '王五',
sex: '女',
time: '2017-08-09' }, {
id: 3,
name: '李四',
sex: '男',
time: '2017-08-09' }, {
id: 4,
name: '楊朝來',
sex: '男',
time: '2017-08-09' }, {
id: 5,
name: '蔣平',
sex: '男',
time: '2017-08-09' }, {
id: 6,
name: '唐燦華',
sex: '男',
time: '2017-08-09' }, {
id: 7,
name: '馬達',
sex: '男',
time: '2017-08-09' }, {
id: 8,
name: '趙小雪',
sex: '女',
time: '2017-08-09' }, {
id: 9,
name: '薛文泉',
sex: '男',
time: '2017-08-09' }, {
id: 10,
name: '丁建',
sex: '男',
time: '2017-08-09' }, {
id: 11,
name: '王麗',
sex: '女',
time: '2017-08-09' }],
columns: [{
field: 'id',
title: '序號' }, {
field: 'name',
title: '姓名',
editable: {
type: 'text',
validate: function (value) { if ($.trim(value) == '') { return '姓名不能為空!';
}
}
}
}, {
field: 'sex',
title: '性別',
editable: {
type: 'select',
pk: 1,
source: [
{value: 1, text: '男'},
{value: 2, text: '女'},
],
noeditFormatter: function (value,row,index) { var result={filed:"sex",value:value,class:"badge",style:"background:#333;padding:5px 10px;"}; return result;
}
}
}, {
field: 'time',
title: '時間',
editable: {
type: 'date',
format: 'yyyy-mm-dd',
viewformat: 'yyyy-mm-dd',
datepicker: {
weekStart: 1 },
noeditFormatter: function (value,row,index) { var result={filed:"time",value:value,class:"badge",style:"background:#333;padding:5px 10px;"}; return result;
}
}
}]
});
關于bootstrap table的導出及使用可以看我另外一篇博客。
下載和引用
下載x-editable,并如下引用。
<link href="js/bootstrap_above/x-editable-develop/dist/bootstrap-editable/css/bootstrap-editable.css" rel="stylesheet"> <script src="js/bootstrap_above/x-editable-develop/dist/bootstrap-editable/js/bootstrap-editable.js"></script> <script src="js/bootstrap_above/bootstrap-table-develop/dist/extensions/editable/bootstrap-table-editable.js"></script>
然后講上訴的一些文件修改添加,就完成了。
另外項目的結果展示
其中的樣式都是自行在x-editable的基礎上添加的。如配置出問題,以下是源碼鏈接。
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>表單標簽及屬性介紹</title>
</head>
<body>
<!--form:表單標簽,在html頁面創建一個表單(瀏覽器上不顯示),若要提交數據到服務器則負責收集數據的標簽要放到form內 ;-->
<!--action:(確定表單提交的路徑);-->
<!--method提交方式:get(默認值)有內容 ,post沒有-->
<form action="#" method="get">
<!--input:輸入域標簽,獲取用戶輸入信息;-->
<!--type值不同收集方式不同:hidden(隱藏字段,數據會發送到服務器但瀏覽器不顯示),text(文本框),password(密碼框),radio(單選框),checkbox(復選框),
file(文件上傳組件),submit(提交按鈕),button(普通按鈕),reset(重置按鈕);-->
<!--name:元素名(表單數據需提交到服務器必提供name屬性值),服務器通過屬性值獲取提交數據;-->
<!--readonly:只讀-->
<!--value:設置input默認值。submit和reset為按鍵上顯示數據-->
<!--size:大小-->
<!--maxlength:允許輸入的最大長度-->
隱藏字段:<input type="hidden" name="id" value=""/><br/>
用戶名:<input type="text" name="username" readonly="readonly" value="zhangsan" size="40px" maxlength="20"/><br/>
密碼:<input type="password" name="password"/><br/>
確認密碼:<input type="password" name="repassword"/><br/>
性別:<input type="radio" name="sex" value="man"/>男
<input type="radio" name="sex" value="woman"/>女<br/>
愛好:<input type="checkbox" name="hobby" value="釣魚"/>釣魚
<input type="checkbox" name="hobby" value="打電動"/>打電動
<!--checked:單選或復選框默認勾選-->
<input type="checkbox" name="hobby" value="畫畫" checked="checked"/>畫畫<br/>
頭像:<input type="file" /><br/>
<!--select:下拉列表標簽-->
籍貫:<select name="province">
<!--option:子標簽,下拉列表中的一個選項-->
<option>---請選擇---</option>
<!--value:發送得服務器的選項值-->
<option value="北京">北京</option>
<option value="上海">上海</option>
<!--selected:勾選當前列表項-->
<option value="廣州" selected="selected">廣州</option>
</select><br/>
自我介紹:
<!--textarea:文本域-->
<textarea>
</textarea><br/>
提交按鈕:<input type="submit" value="注冊"/><br/>
普通按鈕:<input type="button" value="普通按鈕"><br/>
重置按鈕:<input type="reset"/>
</form>
</body>
</html>
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
MVP架構在Android這一塊已經盛行依舊,對于一些學習能力比較強的人來說,已經能夠運用自如甚至改造優化了,對于吾等菜鳥,卻是如此的陌生,今日這篇博客,算是小弟在學習和應用上的一點總結罷了,如有不足,還請各位大神不吝指教。
MVP架構是什么就不多說了,博主主要很大家分享的是,如何設計MVP架構。
先來分析一下MVP如何使用:M-V-P三層之間,P作為中間層,負責M,V之間的數據交互的中介,將數據從M層獲取,處理之后提交到V層,換句話說,V需要持有P的實例,P層需要持有V的實例。原理很簡單,使用泛型對數據進行封裝處理:
2.定義一個P層的接口:
3.封裝P基類:綁定解綁V實例
4.M層封裝:
之后,將數據提交到activity或者fragment就行了。
收工,MVP基礎框架搭建完成了。沒錯,就是基礎框架,但是能不能用呢,讓我們拭目以待吧。
然后是Presneter:
最后來完成Activity的邏輯:
1.定義一個V層的空接口,主要是方便封裝:
/**
* V層接口
*/ public interface IView { }
/**
* 抽象為接口
*
*/ public interface IPresenter<V extends IView> { /**
* 綁定視圖
*
* @param view
*/ void attachView(V view); /**
* 解除綁定(每個V記得使用完之后解綁,主要是用于防止內存泄漏問題)
*/ void dettachView();
}
/**
* 抽象類 統一管理View層綁定和解除綁定
*
* @param <V>
*/ public class BasePresenter<V extends IView, M extends IModel> implements IPresenter<V> { private WeakReference<V> weakView; protected M model;
public V getView() { return proxyView;
} /**
* 用于檢查View是否為空對象
*
* @return */ public boolean isAttachView() { return this.weakView != null && this.weakView.get() != null;
} @Override public void attachView(V view) { this.weakView = new WeakReference<V>(view);
} @Override public void dettachView() { if (this.weakView != null) { this.weakView.clear(); this.weakView = null;
}
}
}
/**
* M層
*/ public interface IModel { } /**
* 登錄model
* Created by admin on 2018/2/5.
*/ public interface ILoginModel extends IModel { void login();
} /**
* 登錄
* Created by admin on 2018/2/5.
*/ public class LoginModel implements ILoginModel { @Override public void login() { // TODO: 2018/2/5 發起登錄請求 }
}
最基本的鋪墊已經做好了,接下來就該封裝View了:
/**
* Created by admin on 2018/2/5.
*/ public abstract class MvpActivity<V extends IView, P extends BasePresenter<V>> extends AppCompatActivity implements IView { private P presenter;
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState);
...
presenter=getPresenter();
presenter.attachView(this);
} protected P getPresenter() { return presenter;
} protected void setPresenter(P presenter) { this.presenter = presenter;
} protected V getView() { return (V) this;
}
...
@Override protected void onDestroy() {
presenter.dettachView();
... super.onDestroy();
}
}
先來寫一個View:
public interface ILoginView extends IView { void onLoginSuccess(); void onFailed();
}
/**
* Created by admin on 2018/2/5.
*/ public class LoginPresenter extends BasePresenter<ILogin, LoginModel> { public LoginPresenter() {
model = new LoginModel();
}
public void login(){
model.login(new LoginCallBack() { @Override public void onSuccess() { if(null!=(ILogin)getView()){
weakView.onLoginSuccess();
}
} @Override public void onFailure() { if(null!=(ILogin)getView()){
weakView.onFailure();
}
}
});
}
}
public class LoginActivity extends MvpActivity<ILoginView, LoginPresenter> implements ILoginView { ...
@Override public LoginPresenter getPresenter() { return new LoginPresenter();
} public void login(View view) {
String name = etUserName.getText().toString();
String pwd = etUserPwd.getText().toString();
getPresenter().login(name, pwd);
}
@Override public void onLoginSuccess() {
}
@Override public void onFailed(){
...
}
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
1、簡介
retrofit是一個封裝okhttp請求的網絡請求庫,可以通過Rxjava適配返回信息。
我們通過Retrofit.Builder建造者模式創建一個Retrofit實例對象
public static final class Builder {
/**
*Android線程切換的類
*/
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
Builder(Retrofit retrofit) {
platform = Platform.get();
callFactory = retrofit.callFactory;
baseUrl = retrofit.baseUrl;
converterFactories.addAll(retrofit.converterFactories);
// Remove the default BuiltInConverters instance added by build().
converterFactories.remove(0);
callAdapterFactories.addAll(retrofit.callAdapterFactories);
// Remove the default, platform-aware call adapter added by build().
callAdapterFactories.remove(callAdapterFactories.size() - 1);
callbackExecutor = retrofit.callbackExecutor;
validateEagerly = retrofit.validateEagerly;
}
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
callAdapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = checkNotNull(executor, "executor == null");
return this;
}
public List<CallAdapter.Factory> callAdapterFactories() {
return this.callAdapterFactories;
}
public List<Converter.Factory> converterFactories() {
return this.converterFactories;
}
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
}
通過Retrofit.Builder中build方法創建一個Retrofit實例對象,在創建Retrofit時會判斷用戶創建OkhttpClient對象,沒有創建Retrofit會創建一個默認okhttpClient對象,然后設置Platform中的主線程線程池,設置線程池處理器交給主線程Looper對象。然后創建一個Retrofit對象。我們通過Retrofit.create創建一個接口代理類
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
}
});
}
在調用Creater方法時,通過代理類創建Service實例對象,當我們通過接口實例對象調用方法時,通過invoke方法時,通過Method創建一個ServiceMethod對象,然后把ServiceMethod存儲起來
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}
private CallAdapter<T, R> createCallAdapter() {
/**
*獲取方法返回值類型
*/
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
//獲取注解信息
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
在創建ServiceMethod時,獲取我們okhttp請求是否有返回值,沒有返回值拋出異常,然后獲取注解信息,然后獲取retrofit中CallAdapter.Factory,然后調用get方法,我們在通過rxjavaFactoryAdapter.create創建的就是實現CallAdapter.Factory對象,然后調用CallAdapter.Factory中respenseType方法,然后通過我們傳遞converter對數據進行序列化,可以通過gson和fastjson進行實例化對象,然后通過parseMethodAnnomation解析請求類型
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
// Get the relative URL path and existing query string, if present.
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError("URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
通過注解類型獲取到請求類型時,通過調用相關方法解析獲取到請求url,然后通過注解獲取方法中是否有注解字段,有注解信息存儲到Set集合中。然后創建一個OkhttpCall對象,通過調用serviceMethod.adapt方法做網絡請求,serviceMethod.adapt調用是callAdapter中的adapt方法,如果用戶沒有設置callAdapter模式使用的是ExecutorCallAdapterFactory中的adapt方法
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
} else {
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
public Type responseType() {
return responseType;
}
public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call);
}
};
}
}
在ExectorCallAdapterFactory中調用組裝的Call方法中enqueue方法調用異步網絡請求,成功后通過Platform中MainThreadExecutor切換到主線程。在調用callback中的enqueue,onResponse和onFairlure方法時實際是調用到OkhttpCall方法的onResponse方法,在OkHttpCall.enqueue中重新組建OkHttp.Call url和參數信息,然后封裝請求,請求成功后通過parseResponse解析返回信息狀態,然后把返回信息狀態成ResponseBody對象,調用ServiceMethod.toResponse解析,在toResponse中實際是我們設置ConverterFactory對象解析數據,完成后調用callBack中onSuccess方法。
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
圖片三個網站的圖片搜索結果進行爬取和下載。
首先通過爬蟲過程中遇到的問題,總結如下:
1、一次頁面加載的圖片數量各個網站是不定的,每翻一頁就會刷新一次,對于數據量大的爬蟲幾乎都需要用到翻頁功能,有如下兩種方式:
1)通過網站上的網址進行刷新,例如必應圖片:
url = 'http://cn.bing.com/images/async?q={0}&first={1}&count=35&relp=35&lostate=r
&mmasync=1&dgState=x*175_y*848_h*199_c*1_i*106_r*0'
2)通過selenium來實現模擬鼠標操作來進行翻頁,這一點會在Google圖片爬取的時候進行講解。
2、每個網站應用的圖片加載技術都不一樣,對于靜態加載的網站爬取圖片非常容易,因為每張圖片的url都直接顯示在網頁源碼中,找到每張圖片對應的url即可使用urlretrieve()進行下載。然而對于動態加載的網站就比較復雜,需要具體問題具體分析,例如google圖片每次就會加載35張圖片(只能得到35張圖片的url),當滾動一次后網頁并不刷新但是會再次加載一批圖片,與前面加載完成的都一起顯示在網頁源碼中。對于動態加載的網站我推薦使用selenium庫來爬取。
對于爬取圖片的流程基本如下(對于可以通過網址實現翻頁或者無需翻頁的網站):
1. 找到你需要爬取圖片的網站。(以必應為例)
2. 使用google元素檢查(其他的沒用過不做介紹)來查看網頁源碼。
3. 使用左上角的元素檢查來找到對應圖片的代碼。
4. 通過觀察找到翻頁的規律(有些網站的動態加載是完全看不出來的,這種方法不推薦)
從圖中可以看到標簽div,class=’dgControl hover’中的data-nexturl的內容隨著我們滾動頁面翻頁first會一直改變,q=二進制碼即我們關鍵字的二進制表示形式。加上前綴之后由此我們才得到了我們要用的url。
5. 我們將網頁的源碼放進BeautifulSoup中,代碼如下:
url = 'http://cn.bing.com/images/async?q={0}&first={1}&count=35&relp=35&lostate=r&mmasync=1&dgState=x*175_y*848_h*199_c*1_i*106_r*0' agent = {'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.165063 Safari/537.36 AppEngine-Google."}
page1 = urllib.request.Request(url.format(InputData, i*35+1), headers=agent)
page = urllib.request.urlopen(page1)
soup = BeautifulSoup(page.read(), 'html.parser')
我們得到的soup是一個class ‘bs4.BeautifulSoup’對象,可以直接對其進行操作,具體內容自行查找。
首先選取我們需要的url所在的class,如下圖:
波浪線是我們需要的url。
我們由下面的代碼得到我們需要的url:
if not os.path.exists("./" + word):#創建文件夾 os.mkdir('./' + word) for StepOne in soup.select('.mimg'):
link=StepOne.attrs['src']#將得到的<class 'bs4.element.Tag'>轉化為字典形式并取src對應的value。 count = len(os.listdir('./' + word)) + 1 SaveImage(link,word,count)#調用函數保存得到的圖片。
最后調用urlretrieve()函數下載我們得到的圖片url,代碼如下:
try:
time.sleep(0.2)
urllib.request.urlretrieve(link,'./'+InputData+'/'+str(count)+'.jpg') except urllib.error.HTTPError as urllib_err:
print(urllib_err) except Exception as err:
time.sleep(1)
print(err)
print("產生未知錯誤,放棄保存") else:
print("圖+1,已有" + str(count) + "張圖")
這里需要強調是像前面的打開網址和現在的下載圖片都需要使用try except進行錯誤測試,否則出錯時程序很容易崩潰,大大浪費了數據采集的時間。
以上就是對單個頁面進行數據采集的流程,緊接著改變url中{1}進行翻頁操作繼續采集下一頁。
數據采集結果如下:
有問題請留言。
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
JS頁面代碼段:
-
const app = getApp()
-
let goodsList = [
-
{ actEndTime: '2018-07-21 21:00:34' },
-
{ actEndTime: '2028-07-17 21:00:37' },
-
{ actEndTime: '2018-09-21 05:00:59' },
-
{ actEndTime: '2018-08-19 07:00:48' },
-
{ actEndTime: '2018-08-28 03:00:11' }
-
]
-
Page({
-
-
data: {
-
countDownList: [],
-
actEndTimeList: []
-
},
-
-
onLoad: function () {
-
let endTimeList = [];
-
// 將活動的結束時間參數提成一個單獨的數組,方便操作
-
goodsList.forEach(o => { endTimeList.push(o.actEndTime) })
-
this.setData({ actEndTimeList: endTimeList });
-
// 執行倒計時函數
-
this.countDown();
-
},
-
-
//當時間小于兩位數時十位數補零。
-
timeFormat: function (param) {//小于10的格式化函數
-
return param < 10 ? '0' + param : param;
-
},
-
-
//倒計時函數
-
countDown: function () {
-
// 獲取當前時間,同時得到活動結束時間數組
-
let newTime = new Date().getTime();//當前時間
-
let endTimeList = this.data.actEndTimeList;//結束時間的數組集合
-
let countDownArr = [];//初始化倒計時數組
-
-
// 對結束時間進行處理渲染到頁面
-
endTimeList.forEach(o => {
-
let endTime = new Date(o).getTime();
-
let obj = null;
-
// 如果活動未結束,對時間進行處理
-
if (endTime - newTime > 0) {
-
let time = (endTime - newTime) / 1000;
-
// 獲取天、時、分、秒
-
let day = parseInt(time / (60 * 60 * 24));
-
let hou = parseInt(time % (60 * 60 * 24) / 3600);
-
let min = parseInt(time % (60 * 60 * 24) % 3600 / 60);
-
let sec = parseInt(time % (60 * 60 * 24) % 3600 % 60);
-
obj = {
-
day: this.timeFormat(day),
-
hou: this.timeFormat(hou),
-
min: this.timeFormat(min),
-
sec: this.timeFormat(sec)
-
}
-
} else {//活動已結束,全部設置為'00'
-
obj = {
-
day: '00',
-
hou: '00',
-
min: '00',
-
sec: '00'
-
}
-
}
-
countDownArr.push(obj);
-
})
-
//每隔一秒執行一次倒計時函數, 渲染
-
this.setData({ countDownList: countDownArr })
-
setTimeout(this.countDown, 1000);
-
}
-
})
wxml頁面代碼段
-
<view class='tui-countdown-content' wx:for="{{countDownList}}" wx:key="countDownList">
-
距結束
-
<text class='tui-conutdown-box'>{{item.day}}</text>天
-
<text class='tui-conutdown-box'>{{item.hou}}</text>時
-
<text class='tui-conutdown-box'>{{item.min}}</text>分
-
<text class='tui-conutdown-box tui-countdown-bg'>{{item.sec}}</text>秒
-
</view>
-
wxss頁面代碼段
-
page{
-
background: #f5f5f5;
-
}
-
.tui-countdown-content{
-
height: 50px;
-
line-height: 50px;
-
text-align: center;
-
background-color: #fff;
-
margin-top: 15px;
-
padding: 0 15px;
-
font-size: 18px;
-
}
-
.tui-conutdown-box{
-
display: inline-block;
-
height: 26px;
-
width: 26px;
-
line-height: 26px;
-
text-align: center;
-
background:#ccc;
-
color: #000;
-
margin: 0 5px;
-
}
-
.tui-countdown-bg{
-
background: red;
-
color: #fff;
-
}
-
-
.container{
-
width: 100%;
-
display: flex;
-
justify-content: center;
-
}
-
.backView{
-
width:690rpx;
-
background: #fff;
-
display: flex;
-
flex-direction: column;
-
margin-bottom: 30rpx;
-
}
-
.createDate
-
{
-
background: #f5f5f5;
-
padding:15rpx 15rpx 10rpx 15rpx;
-
line-height: 50rpx;
-
font-size: 28rpx;
-
color: gainsboro;
-
text-align: center;
-
}
-
.backViewitem1{
-
-
display: flex;
-
flex-direction: row;
-
height: 55rpx;
-
align-items: center;
-
padding:8rpx 40rpx;
-
border-bottom: 2rpx solid #f5f5f5;
-
}
-
.ico
-
{
-
width:35rpx;
-
height:35rpx;
-
}
-
.name
-
{
-
color: #c13176;
-
margin-left: 20rpx;
-
font-size: 28rpx;
-
}
-
-
.details
-
{
-
font-size:24rpx;
-
letter-spacing: 2rpx;
-
-
}
-
.backViewitem2{
-
-
display: flex;
-
flex-direction: row;
-
line-height: 35rpx;
-
min-height: 70rpx;
-
padding: 15rpx 40rpx 10rpx 40rpx;
-
border-bottom: 2rpx solid #f5f5f5;
-
}
-
.details1
-
{
-
color:#888;
-
font-size:23rpx;
-
letter-spacing: 2rpx;
-
-
}
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
藍藍設計的小編 http://www.syprn.cn