zzr-china / blog Goto Github PK
View Code? Open in Web Editor NEWissues as blog
Home Page: https://github.com/ZZR-china/blog/issues
License: MIT License
issues as blog
Home Page: https://github.com/ZZR-china/blog/issues
License: MIT License
我的Git常用命令两三条
当程序猿两年,使用sublime两年,一生最爱sublime
快捷键:
Ctrl+Shift+F
Ctrl+鼠标左键 多光标选中
过滤文件/文件类型:
在where中输入 -dist/,-*.test
在设置中file_exclue_partten加上[dist]
常用的插件
packagecontrol必装,sublime插件管理器
Emmet前端必装,多种便捷快捷键,让你的html飞起来
AutoFileName文件名路径自动补全,妈妈再也不用担心我找不多文件路径了
更多安装包可以到packagecontrol官网查看。这里不罗列了。
使用sublime最大的体会就是轻便,简单,正如蛮大师的话:“随心而动”。
为了更好的用户体验,微信中很多html5页面使用了window.history.pushState方法来实现浏览器的无刷新跳转url
这次的需求是模仿携程的出发城市选择页面改进我们微信端的国家选择页,携程的页面如下:
实现的过程中就发现,在点击了输入框后,页面变成了搜索的样式,如下
这个 时候url是没有变化的,但是当我们在浏览器点击返回按钮,又回到了最开始的页面,而不是变化url,回到入口页面。
在控制台输入window.history可以发现点击输入框后history的length增加了1,如下图:
这里可以猜测下,携程的前端应该是使用window.history.pushState这个api,当用户点击输入框时便在history中压入一个当前页面的url,并监听浏览器返回事件,关闭搜索UI组件,根据猜测,可以写出一段代码(为了方便,这里使用jquery):
$(function() {
$('searchInput').onClick(function () {
$('searchUI').show();
var state = {
title : "title",
url : "#"
};
window.history.pushState(state, "title", "#");
})
window.addEventListener("popstate", function(e) {
$('searchUI').hide()
}, false);
我这里的微信端项目使用的是vue。其实可以直接让点击后跳转到一个新的页面然后通过vuex传递值,但是考虑到组件的通用性,决定将国家选择功能封装成子组件,并在其中引入pushState这一特性,优化用户体验。
首先,创建SelectCountry.vue文件
// ./src/components/SelectCountry.vue
<template>
<div class="selectcountry">
</div>
</template>
<script>
export default {
name: 'selectcountry',
props: {
show: {
type: Boolean,
default: true
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="less" scoped>
</style>
并在main.js加载并定义这一组件
// ./main.js
import SelectCountryfrom './components/SelectCountry.vue'
Vue.component('select-country', SelectCountry)
之后我们在AddAddress.vue(添加用户地址)这个页面中引入选择国家组件,假设添加地址这一页面的路由为 localhost:3000/#/address,那么当选择国家组件展示时(组件是全页面布局,引入时默认隐藏 v-show="false")才会去pushState,组件出于created这一生命周期时不会对页面history state做改变。监听控制组件展现/隐藏的props--show。代码大致如下:
// ./src/components/SelectCountry.vue
watch: {
async show (val) {
if !(val) return
// 压入一个新的history
const url = '#' + this.$route.fullPath
const title = 'selectcountry'
const state = {
title,
url
}
window.history.pushState(state, title, url)
}
}
同时,需要监听浏览器的返回事件,在手机端也是用户点击返回键的时候,关闭这一组件,在原先的代码中添加:
// ./src/components/SelectCountry.vue
watch: {
async show (val) {
if !(val) return
// 压入一个新的history
const url = '#' + this.$route.fullPath
const title = 'selectcountry'
const state = {
title,
url
}
window.history.pushState(state, title, url)
const _this = this
window.addEventListener('popstate', function (e) {
// 此时的this指的是window, 所以要将_this传入进来
const vue = _this
vue.$emit('listenCountryClose', false)
}, false)
}
}
通过$emit方法,调用父组件中的方法将show转为false,关闭子组件(选择国家组件)。
这种方式,没有用到vuex,子组件只跟引用的父组件交互,组件的复用率可以提高不少。Vue的确很方便,还需要多加研究才是。
gulp虽然在新的项目中不用了,但是第一次接触它时的惊喜和它带给我的便利,我会一直记得,以后的项目里相信还会有它的用武之地
今天运行了下官网项目,发现报错
require.extensions.hasOwnProperty(ext) is not a function
排查后,认定是因为node版本升级到8.4.0的原因(之前这个项目是在node 6.0.0运行)。查询node官网发现, node 8.4.0中 require.extensions API被废弃,临时解决方法:在 node_modules/require-dir/index.js中注释掉require.extensions(没错,就是这么简单粗暴)。
查询时引申出一个问题,就是gulp任务过多时的处理。之前的项目中,使用了requireDir 来加载分解成单个任务的gulp task文件。一个将开发文件夹下js文件复制到生产dist目录下的task如下:
var gulp = require('gulp');
var notify = require('gulp-notify');
var config = require('../../../config').scripts;
// process JS files and return the stream.
gulp.task('lib-scripts', function() {
return gulp.src(config.libjs)
.pipe(gulp.dest('dist/assets/js/lib'))
.pipe(notify({ message: 'lib-scripts task complete' }));
});
requireDir 会自动加载,gulp任务可以直接调用。gulpfile.js中代码如下:
var requireDir = require('require-dir');
// Require all tasks in gulp/tasks, including subfolders
requireDir('./gulp/tasks', { recurse: true });
其实完全可以不使用require-dir,直接将每个任务写成模块,然后在gulpfile.js中使用glob来调用。还用上面的复制js文件的任务为例,完全可以写成
module.exports = function (gulp, config, plugins) {
return gulp.task('lib-scripts', function () {
return gulp.src(config.libjs)
.pipe(gulp.dest('dist/assets/js/lib'))
.pipe(plugins.notify({ message: 'lib-scripts task complete' }));
});
}
之后在gulpfile,js中引入gulp和config还有gulp-load-plugins, glob读取js文件并require。
离开ejs后,我涌入了前后端分离的大潮,虽然一头雾水,但是不拥抱变化就只能被时代抛弃,尤其是在技术领域
上一篇 最后说道我们准备以hapi框架为基础开发后端api服务,前端使用react-native开发客户端。这是我第一次在工作中接触到前后端分离的模式,给前端开发restful类型的api,当时可以说一片茫然,每天都是疯狂的看文档,搜谷歌,逛知乎。
记得当时详细看了知乎上的这个问题: Web 前后端分离的意义大吗? 和 怎样用通俗的语言解释REST,以及RESTful? 获益良多,hapi框架的文档 也很详细,天生的restful api服务框架,开发起来很舒服。
记得我当时的任务就是从mongo中提取数据然后返回给前端,为了规范还用上了put、patch、delete,记得当时一个前端还很烦的说直接用get/post不就好,加这么多有什么用,我跟他解释了这是restful规范,还好技术老大很支持我。开发很顺利的进行下去。直到我们开始做用户状态的保存即登陆的时候。
上篇有提到,模板引擎开发下,用户登陆功能的开发很简单。但是前后端分离后,一切都不一样,app客户端还好些,要是前端和后端的域名端口不同,限制于浏览器同源策略,session和cookie都很难派上用场。大家可以自行搜索 session的跨域问题? 前后端分离时如何保存用户状态?。
我们当时做的是ipad端app,其实session和cookie还是可以用上的,但是我在搜索时发现 JWT 这个东西,出于好奇,便在项目中用上了它。
JWT是一种规范,很多后端框架都有实现jwt的方法,hapi也有相应的插件去实现。大概的流程是:前端传输用户名和密码,后端返回加密后的token,token中含有用户的基本信息和过期认证,之后前端将获取的token保存在本地localStorage(web端)或是缓存(app端)中,之后的请求都要将token放在http header中。后端不保存用户登陆信息,只判断token。安全性也是杠杠的,看github上的star就知道,选它,没错的。
我当时选用的是hapi的token插件hapi-auth-jwt2,使用hapi的注册中间件方法直接引入:
server.register(hapi_auth_jwt2, (err) => {
if (err) {
console.log(err);
}
server.auth.strategy('jwt', 'jwt', {
key: secret,
validateFunc: validate1,
verifyOptions: { algorithms: ['HS256'] }
});
server.auth.default('jwt');
});
server.auth是hapi中定义权限验证的方法。定义路由时可以默认选用:
const login = (req, reply) => {
var staff = null,
msg = new message();
if (req.auth.isAuthenticated) {
return reply(msg.success('你已登录!'));
}
staff = {
store_id : req.payload.store_id,
job_number: req.payload.job_number,
password : req.payload.password
};
};
module.exports = [{
method: ['GET', 'POST'],
path : '/login',
config: {
handler: login,
plugins: { 'hapi-auth-cookie': { redirectTo: false } } ,
description: '<p>员工/管理员登陆</p>'
}
}];
req.auth.isAuthenticated是判断headers中是否传了token,要想后去解析后的token可以使用req.auth.credentials 这个方法中存储了解码后的信息。
之前公司面试的时候,我经常会问面试者一个问题,URL和URI的区别是什么?现在这个问题得再加一个了,URL、URI和URN的区别是什么?
之前只对URL和URI有概念,知道URI包含着URL。URN也是URN的一个子集,他们的关系可以看下图:
一个常见的URI的例子是:
https://www.baidu.com/index.html#intro
其中https://是定义访问的方式,www.baidu.com/index.html是定义资源存放的位置,而#intro就是资源。
URL是URI的一个子集,告诉我们访问网络位置的方式。在我们的例子中,URL应该如下所示:
https://www.baidu.com/index.html
URN是URI的子集,包括名字(给定的命名空间内),但是不包括访问方式,如下所示:
www.baidu.com/index.html#intro
如果你忘记了这篇文章的内容,至少要记住一件事:URI可以被分为URL、URN或两者的组合。如果你一直使用URI这个术语,就不会有错。
参考:
我一直坚信,不论从事什么职业,做着什么样的工作,不要被单一的方向局限,尤其是程序员。这么一个可能性无限的职业,如果说只是做前端,只是做后端,不是太无趣了吗
这两年来,从最初的java,实习期的js前端(meteor),第二份工作的nodejs后端(express、hapi)。最后到了现在的回归前端(vue、react),当然,还有平时接单时的php(thinkphp)。经历了很多,学的很杂,学精的暂时还没。
幻想着自己有一天可以成为一个自由自在的人,我想做的想学的全凭兴趣,不在乎别人的目光,不在乎什么TIOBE编程语言排名,不在乎什么风口。
世界这么大,我去过的地方很少;计算机的世界这么大,我了解的很少。
现在我们在浏览器访问facebook或是qq.com都会跳出弹出框,询问是否允许网站推送桌面通知。
如果点击允许那么过一段时间就会收到网站的推送,往往是一张图片加几句描述性文字,然后点击即跳转到网页上。如下图:
在MDN上查到是使用 Notifications API实现的,介绍如下:Notifications API 的通知接口用于向用户配置和显示桌面通知。
MDN还很贴心的举了一个例子:
<button onclick="notifyMe()">Notify me!</button>
<script>
function notifyMe() {
// Let's check if the browser supports notifications
if (!("Notification" in window)) {
alert("This browser does not support desktop notification");
}
// Let's check if the user is okay to get some notification
else if (Notification.permission === "granted") {
// If it's okay let's create a notification
var notification = new Notification("Hi there!");
}
// Otherwise, we need to ask the user for permission
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
// If the user is okay, let's create a notification
if (permission === "granted") {
var notification = new Notification("Hi there!");
}
});
}
// At last, if the user already denied any notification, and you
// want to be respectful there is no need to bother them any more.
}
</script>
其工作流程是检测浏览器是否支持notification api, 没有的话会alert一段报错信息。支持的话会检测用户是否同意执行,Notification.requestPermission 这个方法用于向用户请求获得消息提醒的权限,调用这个方法将产生如下效果,分别对应着3中状态:“granted”(状态值:0)表示用户同意消息提醒;“default”(状态值:1)表示默认状态,用户既未拒绝,也未同意;“denied”(状态值:1)表示用户拒绝消息提醒。只有在状态值为0的时候才能够允许消息提醒,这个值保存在一个内部变量中,并且是只读的,通过checkPermission()方法可以提取到这个状态值。
本文借鉴
_原文地址 http://yjy.people.com.cn/n/2014/0120/c245079-24169402.html _
一般认为Web2.0(论坛、博客为代表)和Web3.0(社交平台、微博客为代表)的相继流行,UGC(User-generated Content,用户生产内容,也称UCC,User-created Content)功不可没。随着移动互联网的发展,网上内容的创作又被细分出PGC(Professionally-generated Content,专业生产内容,也称PPC,Professionally-produced Content)和OGC(Occupationally-generated Content,职业生产内容),甚至有UGC、PGC和OGC谁是主 流的讨论。
因此,UGC和PGC的区别,是有无专业的学识、资质,在所共享内容的领域具有一定的知识背景和工作资历。PGC和OGC的区别,相对容易,以是否领取相应报酬作为分界,PGC往往是出于“爱好”,义务的贡献自己的知识,形成内容;而OGC是以职业为前提,其创作内容属于职务行为。是否有非专业的OGC?
从上图也看到UGC和OGC没有交集。在一个平台(网站)上,用户和提供商总是相对的,两者之间,既是该平台的用户也是该平台的提供商的角色可能有,但属于极少的群体。
以OGC为代表的网站如各大新闻站点、视频网站,其内容均有内部自行创造和从外部花钱购入版权;以UGC为代表的网站如各大论坛、博客和微博客站点,其内容均由用户自行创作,管理人员只是协调和维护秩序;PGC则在这两种网站中都有身影,由于其既能共享高质量的内容,同时网站提供商又无需为此给付报酬,所以OGC站点和UGC站点都很欢迎PGC。
显然,PGC是稀缺的,由于内容的生产是需要成本的(时间、人力和物料),不给付报酬恐难维继,而给付报酬的PGC则归属到OGC的范畴。无论是以内容提供见长的新闻站点、视频网站,还是以互动服务见长的社区、社交站点,都努力争取更多的PGC。
或许PGC只是业界的一种错觉,根本上来看,PGC是UGC中的一部分,只是这部分内容相当精彩。互联网内容供应仍是泾渭分明的UGC和OGC。
React的服务端同构被搁置了,后台管理系统的优化的确不是紧要的事情,毕竟to B,但是微信端项目的服务端优化迫在眉睫,一个用Vue搭建的spa项目
建议大家阅读本文前可以先看这篇文章:【第1057期】服务端与客户端同构 —— Vue.js 应用框架 Nuxt.js,我也是看了这篇和 官网 的介绍才对nuxt有了初步的了解。
2016 年 10 月 25 日,zeit.co 背后的团队对外发布了 Next.js,一个 React 的服务端渲染应用框架。几小时后,与 Next.js 异曲同工,一个基于 Vue.js 的服务端渲染应用框架应运而生,我们称之为:Nuxt.js。
Nuxt.js 是一个基于 Vue.js 的通用应用框架。
通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 UI渲染。
我们的目标是创建一个灵活的应用框架,你可以基于它初始化新项目的基础结构代码,或者在已有 Node.js 项目中使用 Nuxt.js。
Nuxt.js 预设了利用Vue.js开发服务端渲染的应用所需要的各种配置。
除此之外,我们还提供了一种命令叫:nuxt generate,为基于 Vue.js 的应用提供生成对应的静态站点的功能。
我们相信这个命令所提供的功能,是向开发集成各种微服务(miscroservices)的 Web 应用迈开的新一步。
作为框架,Nuxt.js 为 客户端/服务端 这种典型的应用架构模式提供了许多有用的特性,例如异步数据加载、中间件支持、布局支持等。(以上文字抄自 Nuxt.js官网 👍 )
简单跟着官网教程走了一遍,发现确实是我一直寻找的Vue同构方式,而且提供了服务端同构和生成静态页面两种部署方式。功能强大,api简洁,兼具了Next.js和Jekyll的功能。之后会慢慢将原来的项目使用Nuxt.js重构,到时候会多放些代码和解决问题的思路与方法上来。
今天和后端搞了一下午的session跨域问题,苦思bug解决之法时回想起以前模板引擎的美好来,仅以此篇献给逝去的 ejs、handlebars、pug...
服务端使用模板引擎时,前后端同源,前端直接把页面样式写好甩给后端,后端根据使用的模板引擎,将数据套进去,没什么跨域问题。这时候session和cookie可以大显身手,用户的各种信息直接放在里面,长期保存的放cookie,短期的像什么登陆验证码之类的就放session。
之前做nodejs后端开发,公司用的是express+ejs,每次开发什么新功能都是前端妹子把页面写好,然后我来处理页面的数据交互等问题,有时候我还要帮她处理些js bug,想想当时还能跟妹子交流真是开心(现在的公司技术清一色的汉子)。记得当时有个全局的处理函数,是用来判断用户的登陆状态的,简单写下:
var express = require('express'),
router = express.Router(),
_buylimit = require('../service/buylimit.service'),
login_help = require('../helpers/login_help');
router.route('/buylimits')
.get(function(req, res) {
login_help(req, res, function() {
_buylimit.getAll(function(buylimits) {
res.render('buylimits', {buylimits: buylimits});
});
});
})
这里的res.render就是express中的渲染函数,直接将buylimit这个数据集合放到ejs文件中。login_help嵌套在render之前用来判断session中是否有用户的登陆信息,有的话继续,没有的话重定向到login界面,login_help的代码如下:
var help = function(req, res, next) {
var admin = req.session.admin;
if(!admin) {
res.redirect('/dashboard/login');
return;
} else {
next();
}
};
module.exports = help;
req.session这个方法可以直接获取session中数据。其实这里应该把login_help当作中间件,然后将判断的结果放到req中去的。express+ejs的确是很方便的组合,当时的技术老大使用这一组合只用了一个月就开发出了公司的产品框架,包含微信端和后台管理页面,将快速开发诠释到了极致。不过后期的各种报错就不谈了,程序员的报错不叫报错,叫八阿哥。
为了让观众更好的理解模板引擎下的前后端开发方式,下面简单实现一个express+ejs的登陆。首先上express后端代码。
var express = require('express'),
router = express.Router(),
admin_service = require('../service/admin.service'),
login_help = require('../helpers/login_help'),
message = require('../helpers/message');
router.route('/login')
.get(function(req, res) {
var admin = req.session.admin;
if(admin) return res.redirect('/');
res.render('login');
})
.post(function(req, res) {
var user = {
username: req.body.username,
password: req.body.password
},
msg = new message();
admin_service.login(user, function(bo, admin) {
if(bo) {
msg.msg = '登录成功!';
msg.status = 1;
req.session.admin = admin;
} else {
msg.msg = '登录失败!';
}
res.send(msg);
});
});
router.route('/logout')
.get(function(req, res) {
login_help(req, res, function(){
req.session.admin = null;
res.send(true);
});
});
module.exports = function(app) {
app.use('/', router);
};
路由 get /login渲染了登陆页面,并做了事先的判断,post /login是登陆页面中的提交事件,判断username和userpassword是否正确,与数据库中的记录进行比对。再看前端页面:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit|ie-comp|ie-stand">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<title>后台登录 - Foowala</title>
</head>
<body>
<input type="hidden" id="TenantId" name="TenantId" value="" />
<div class="loginWraper">
<div id="loginform" class="loginBox">
<div class="form form-horizontal">
<div class="row cl">
<label class="form-label col-xs-3"><i class="Hui-iconfont"></i></label>
<div class="formControls col-xs-8">
<input id="username" name="username" type="text" placeholder="账户" class="input-text size-L radius">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-3"><i class="Hui-iconfont"></i></label>
<div class="formControls col-xs-8">
<input id="password" name="password" type="password" placeholder="密码" class="input-text size-L radius">
</div>
</div>
<div class="row cl">
<div class="formControls col-xs-8 col-xs-offset-3">
<input id="login" name="login" type="submit" class="btn btn-primary radius size-L" value=" 登 录 ">
</div>
</div>
</div>
</div>
</div>
<div class="footer">Copyright Foowala by admin.v1.0</div>
<script type="text/javascript">
app.login();
</script>
</body>
</html>
因为在同一域名下,不需要担心什么跨域问题,点击按钮时候直接ajax上传username和password,然后根据返回的参数进行处理即可。整个开发流程很顺畅,当然,这只是一个最简单的登陆模块的开发。管中窥豹,还是能看到这种模式的优点。
当时express没用了几天就换到了hapi框架上去,hapi用起来还是很爽的。当时使用hapi框架是为了开发的一个ipad端的应用,前端使用的是react-native,后端是nodejs hapi框架,算是第一次接触前后端分离,以一个后端的身份。
React被用来搭建公司的后台管理页面,相对来说对加载速度等要求较低,之后会研究关于vue的同构,毕竟用户端的产品使用vue构建的
前后端分离虽然非常便利,但是前端的加载速度,性能等都是问题,为了更好的用户体验,服务端同构是很有必要的
这段时间,一共开发了两个后台管理平台,用的都是阿里的antd-design。开发过程很舒畅,的确如官网介绍所说:这是一套致力于提升『用户』和『设计者』使用体验的中后台设计语言。
但是开发完毕的部署遇到了些问题,nginx代理后,发现初始化速度很慢,一般需要3s左右,用chrome控制台进行加载速度检测,结果如下:
webpack的一些优化也已经做了,路由也是按需加载,这些也只是指标不治本,要想实现页面秒出即直出,唯有同构,利用服务端的帮助将优化做到极致。
React Stateless Component中是禁止使用refs的,这就导致一些需要操作dom的情况变得复杂起来。比较简单的替代方法是使用一个string来替代ref,例如:
import React from 'react'
export default ({ onChange }) => {
let cityInput
const onSubmit = e => {
e.preventDefault()
onChange(cityInput.value)
}
return (
<form onSubmit={ onSubmit }>
<input type='text' placeholder='Enter City Name'
ref={ el => cityInput = el } />
<button>Go!</button>
</form>
)
}
但是react官方说明
Note:
Although string refs are not deprecated, they are considered legacy, and will likely be deprecated at some point in the future. Callback refs are preferred.
string refs这个特性是有可能被弃用的,所以只好寻找别的方法。
const onSubmit = fn => e => {
e.preventDefault()
const city = e.target.city.value // Access elements through `form`
if (city) {
fn(city)
}
}
const MyComponent = ({
onChange
}) => {
return (
<div>
<form onSubmit={onSubmit(onChange)}>
<input type='text' name='city' placeholder='Enter City Name' />
<button>Go!</button>
</form>
</div>
)
}
很多时候,后台管理有将数据导出到excle文件的需求,这是因为大多管理人员还是熟悉用word excel来办公(或者说是因为web端的数据处理功能还远远没有桌面的报表处理软件强大)。总之,最近接到了添加一个导出数据到excel表的需求,昨天也实现了,下面简单记录下实现的过程。
excel表或者说word excel和wps支持的excel表格式是有很多种的,常规的是xls、xlsx,但是这两种在除C#环境外的后端程序中都不易生成,需求方的系统是php,于是我把目光放到了csv上。
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。通常都是纯文本文件。建议使用WORDPAD或是记事本(NOTE)来开启,再则先另存新档后用EXCEL开启,也是方法之一。
CSV文件格式的通用标准并不存在,但是在RFC 4180中有基础性的描述。使用的字符编码同样没有被指定,但是7-bitASCII是最基本的通用编码。
(以上两端内容来自百度,没错我就是凑字数,你打我呀)
无论是word excel还是wps都是支持csv文件的,所以我们可以放心大胆的使用。选好了要生成的文件格式,下面就简单了。
这次的需求来自一个php网站,所以用php来举例。
public function export_csv () {
$list = $GLOBALS['db']->getAll("select * from supplier");
$order_value = array(
'id'=>'""',
'user_name'=>'""',
'contact_name'=>'""',
'contact_sex'=>'""',
'contact_mobile'=>'""',
'create_time'=>'""');
$content = iconv("utf-8","gbk","商户ID,商户用户名,姓名,性别,手机号,联系座机注册时间");
$content = $content . "\n";
if($list) {
foreach($list as $k=>$v) {
$contact_sex = $v['contact_sex'] == 1 ? '男性' : ($v['contact_sex'] == 0 ? '女性' : '未知');
$create_time = to_date($v['create_time']);
$order_value = array();
$order_value['id'] = '"' . $v['id'] . '"';
$order_value['user_name'] = '"' . iconv('utf-8','gbk',$v['user_name']) . '"';
$order_value['contact_name'] = '"' . iconv('utf-8','gbk',$v['contact_name']) . '"';
$order_value['contact_sex'] = '"' . iconv('utf-8','gbk',$contact_sex) . '"';
$order_value['contact_mobile'] = '"' . iconv('utf-8','gbk',$v['contact_mobile']) . '"';
$order_value['create_time'] = '"' . iconv('utf-8','gbk',$create_time) . '"';
$content .= implode(",", $order_value) . "\n";
}
}
header("Content-type:application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=supplier.csv");
echo $content;
}
iconv是封装的一个将utf-8转换成中文格式的方法,整体的思路就是将数据表中查询出的数据,通过循环,赋值等处理转换成csv的格式,然后输出文件。
看来服务端生成excel文件也没想象中那么难。
随着自己对Vue熟练度的加深,各种新的需求,新的UI组件,以往老的Vue库已经不能满足我的要求,所以,我决定,开发属于自己的Vue组件库,并发布到npm上去
这个步骤算是基础但是比较简单,照着npm官网的教程就行。记得之前做nodejs后端的时候就发布过一个,是用来在服务端分页用的。 mn-paginate写的很简陋,这里就不多说了。简单说下步骤,大概是4步:
mkdir npm-pack&&cd npm-pack
npm init
npm login
npm publish
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.