如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
<!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.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的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.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的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.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的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.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的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.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
一、什么是跨域访问
举个栗子:在A网站中,我们希望使用Ajax来获得B网站中的特定内容。如果A网站与B网站不在同一个域中,那么就出现了跨域访问问题。你可以理解为两个域名之间不能跨过域名来发送请求或者请求数据,否则就是不安全的。跨域访问违反了同源策略,同源策略的详细信息可以点击如下链接:Same-origin_policy;
总而言之,同源策略规定,浏览器的ajax只能访问跟它的HTML页面同源(相同域名或IP)的资源。
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script>
元素是一个例外。利用<script>
元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。更具体的原理需要更多篇幅的讲解,小伙伴可以自行去百度。
JQuery Ajax对JSONP进行了很好的封装,我们使用起来很方便。前端示例:
$.ajax({
type:"GET",
url:"http://www.deardull.com:9090/getMySeat", //访问的链接 dataType:"jsonp", //数据格式设置为jsonp jsonp:"callback", //Jquery生成验证参数的名称 success:function(data){ //成功的回调函数 alert(data);
},
error: function (e) { alert("error");
}
});
需要注意的地方是:
后端要配合使用jsonp,那么首先得了解Jquery Ajax jsonp的一个特点:
Jquery在发送一个Ajax jsonp请求时,会在访问链接的后面自动加上一个验证参数,这个参数是Jquery随机生成的,例如链接
http://www.deardull.com:9090/getMySeat?callback=jQuery31106628680598769732_1512186387045&_=1512186387046
中,参数callback=jQuery31106628680598769732_1512186387045&_=1512186387046
就是jquery自动添加的。
添加这个参数的目的是唯一标识这次请求。当服务器端接收到该请求时,需要将该参数的值与实际要返回的json值进行构造(如何构造下面讲解),并且返回,而前端会验证这个参数,如果是它之前发出的参数,那么就会接收并解析数据,如果不是这个参数,那么就拒绝接受。
需要特别注意的是这个验证参数的名字(我在这个坑上浪费了2小时),这个名字来源于前端的jsonp参数的值。如果把前端jsonp参数的值改为“aaa”,那么相应的参数就应该是
aaa=jQuery31106628680598769732_1512186387045&_=1512186387046
知道了Jquery Ajax Jsonp的原理,也知道了需要接受的参数,我们就可以来编写服务器端程序了。
为了配合json,服务器端需要做的事情可以概括为两步:
根据与前端Ajax约定的jsonp参数名来接收验证参数,示例如下(使用SpringMVC,其他语言及框架原理类似)
@ResponseBody @RequestMapping("/getJsonp") public String getMySeatSuccess(@RequestParam("callback") String callback){
将接收的的验证参数callback与实际要返回的json数据按“callback(json)”的方式构造:
@ResponseBody
@RequestMapping("/getMySeat") public String getMySeatSuccess(@RequestParam("callback") String callback){
Gson gson=new Gson(); //google的一个json工具库 Map<String,String> map=new HashMap<>(); map.put("seat","1_2_06_12"); return callback+"("+gson.toJson(map)+")"; //构造返回值 }
最终,前后端的相应代码应该是这样的:
前端
$.ajax({
type:"GET",
url:"http://www.deardull.com:9090/getMySeat", //访问的链接 dataType:"jsonp", //数据格式设置为jsonp jsonp:"callback", //Jquery生成验证参数的名称 success:function(data){ //成功的回调函数 alert(data);
},
error: function (e) { alert("error");
}
});
后端
@ResponseBody
@RequestMapping("/getMySeat") public String getMySeatSuccess(@RequestParam("callback") String callback){
Gson gson=new Gson(); Map<String,String> map=new HashMap<>(); map.put("seat","1_2_06_12");
logger.info(callback); return callback+"("+gson.toJson(map)+")";
}
需要注意的是:
完整的示例就是上面两段代码,这里就不提供Github连接了。上面的示例亲测有效,如果有遇到问题的,欢迎留言提问。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
有时候当小程序向后台拿数据是一篇html标签的文章时,把它放进小程序会发现很多标签就不兼容,如果要一个个改又很麻烦,有没有方法可以很快地兼容html标签呢?
有个工具可以做到:wxParse
下载了它的压缩包后解压,复制wxParse文件夹放到小程序pages页面里:
在wxml里引入,这里的路径仅供参考:
<import src="../../../../wxParse/wxParse.wxml" /> <view> //在需要放置html文本的地方使用wxParse模板 <template is="wxParse" data="{{wxParseData:content.nodes}}" /> </view>
在js里引入:
let wxparse = require("../../../../wxParse/wxParse.js");
Page({ /**
* 页面的初始数据
*/ data: {
content: '' },
onLoad: function(options) { var that = this;
..... /**
* WxParse.wxParse(bindName , type, data, target,imagePadding)
* 1.bindName绑定的数据名(必填)
* 2.type可以为html或者md(必填)
* 3.data为传入的具体数据(必填)
* 4.target为Page对象,一般为this(必填)
* 5.imagePadding为当图片自适应是左右的单一padding(默认为0,可选)
*/ wxparse.wxParse('content', 'html', result.data.content, that);
}
在wxss引入:
@import "../../../../wxParse/wxParse.wxss";
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
亲测必须将代码放在记事本中,改成html格式,在用IE运行(必须)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>显示指定驱动器的大小及可用空间</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<form name="form1" method="post" action="">
盘符:
<input type="text" name="text1">
<input type="button" name="Button1" value="磁盘空间" onclick="DriveSize(document.form1.text1)">
</form>
<script language="javascript">
<!--
function DriveSize(Drivename)
{
var fso=new ActiveXObject("Scripting.FileSystemObject");
var s=fso.GetDrive(Drivename.value);
if (s.IsReady)
{
var str,str1,AllSize=0.0;
str="当前驱动器的名称为:"+s.DriveLetter+"\n";
AllSize=s.TotalSize/1024/1024/1024;
str=str+"当前驱动器的大小为:"+parseInt(AllSize*10)/10+"\n";
AllSize=s.FreeSpace/1024/1024/1024;
str=str+"当前驱动器的可用空间为:"+parseInt(AllSize*10)/10;
alert(str);
}
else
alert("该驱动器无效。")
}
//-->
</script>
</body>
</html>
运行:
结果:单位为g
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
Bootstrap Table实现定时刷新数据
推荐第二种方法
1、毁掉表格,再查询数据后append,如果你查大量的数据(例如:查询很多渠道的信息),而获取服务器数据又太慢,你就会看到表格在一行一行的增加
setInterval(function() { queryAll();
}, 30000);
function queryAll() { updateRealTimeData();
.
.
.
.
}
function updateRealTimeData() { if(errorFlag || appid == -1) return; //把表格的tbody移除,不然后面会一直添加 $("#realTimeTable").bootstrapTable('removeAll'); //获取数据 $.ajax({
data: { //向服务器发送的一些参数,像日期,游戏ID什么的 .
.
.
.
.
},
type: "post", //url不用说了吧,否则不知道向服务器哪个接口发送并请求 url: *******,
async: true, //超时时间 timeout:30000,
success: function(msg) { if(msg.code == '1') { //定义的函数实现对表格赋值,自定义想传的参数,但别忘了msg,不然搞个屁 showTableData(msg,……);
}
}
});
}
function showTableData(msg,……) { tableData = []; for(var i = 0; i < json.length; i++) {
tableData.push({ //这里也就是data-field的名称,getDate是服务器返回的字段名 date: json[i].getDate,
.
.
.
.
}) //数组反向排列,看情况使用 tableData.reverse(); //向tbody里面添加数据 $("#realTimeTable").bootstrapTable('append', tableData);
}
}
2、使用updateRow方法
setInterval(function() { queryAll(); for (var j = 0; j < 请求的数据的总条数(也就等于表格的行数); j++) {
changeAllChannelRealTime(j, .....);
} }, 30000);
function changeAllChannelRealTime(j, .....) {
$.ajax({
data: { //向服务器发送的一些参数,像日期,游戏ID什么的 .
.
.
.
.
},
type: "post", //url不用说了吧,否则不知道向服务器哪个接口发送并请求 url: *******,
async: true, //超时时间 timeout:30000,
success: function(msg) { if (msg.code == '1') {
changeData(j, msg, .....);
}
},
error: function () { msgToast.error("查询数据出错");
}
});
}
function changeData(i,msg,......){ $('#realTime_Table').bootstrapTable('updateRow', { //i表示第几行,从0开始 index: i,
row: { //这里也就是data-field的名称,*表示字段名 date: msg.*
.
.
.
.
}
});
}
大象~ 大象~ 你的鼻子怎么那么长~~
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
关于 node 环境升级到 v8^ 以上,node-sass 报错的解决方法
今天给同事电脑升级了一下系统,顺便升级了所有的软件,发现原来好好的项目报错了。报错大致信息如下:
ERROR Failed to compile with 1 errors 下午1:56:26 error in ./src/components/Hello.vue Module build failed: Error: Missing binding /Users/fungleo/Sites/MyWork/vuedemo2/node_modules/node-sass/vendor/darwin-x64-57/binding.node Node Sass could not find a binding for your current environment: OS X 64-bit with Node.js 8.x Found bindings for the following environments: - OS X 64-bit with Node.js 6.x This usually happens because your environment has changed since running `npm install`. Run `npm rebuild node-sass --force` to build the binding for your current environment.
at module.exports (/Users/fungleo/Sites/MyWork/vuedemo2/node_modules/node-sass/lib/binding.js:15:13) at Object.<anonymous> (/Users/fungleo/Sites/MyWork/vuedemo2/node_modules/node-sass/lib/index.js:14:35)
at Module._compile (module.js:635:30) at Object.Module._extensions..js (module.js:646:10) at Module.load (module.js:554:32) at tryModuleLoad (module.js:497:12) at Function.Module._load (module.js:489:3) at Module.require (module.js:579:17) at require (internal/module.js:11:18) at Object.<anonymous> (/Users/fungleo/Sites/MyWork/vuedemo2/node_modules/sass-loader/lib/loader.js:3:14)
at Module._compile (module.js:635:30) at Object.Module._extensions..js (module.js:646:10) at Module.load (module.js:554:32) at tryModuleLoad (module.js:497:12) at Function.Module._load (module.js:489:3) at Module.require (module.js:579:17) @ ./~/vue-style-loader!./~/css-loader?{"minimize":false,"sourceMap":false}!./~/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-2d1bdf0c","scoped":false,"hasInlineConfig":false}!./~/sass-loader/lib/loader.js?{"sourceMap":false}!./~/vue-loader/lib/selector.js?type=styles&index=0!./src/components/Hello.vue 4:14-394 13:3-17:5 14:22-402 @ ./src/components/Hello.vue
@ ./src/router/index.js
@ ./src/main.js
@ multi ./build/dev-client ./src/main.js
> Listening at http://localhost:8080
这段代码是我升级node之后,在我的电脑上复制出来的。但大概就是这么个意思,里面根据不同的项目位置什么的,会有所不同。
简单的说,这段代码就是告诉你,node-sass
不兼容 node v8
的版本。那就很好解决了。在当前项目下面执行
npm i node-sass -D
然后项目就恢复正常了。
当项目出错之后,不要着急,仔细看下报错代码,实在不行用翻译工具翻译一下。一般来说,是很快能够找到解决方法的。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。蓝蓝设计的小编 http://www.lanlanwork.com