个人资料的访问地址是:user/profile
效果是:
![2015-06-28/558fe9f6a5a39](http://box.kancloud.cn/2015-06-28_558fe9f6a5a39.png)
主要分普通信息和头像裁剪外加第三方登录绑定。radio选中绑定用了eq标签。
主要表单代码:
~~~
<form action="/api.php/member/{$info.id}" method="POST" class="MemberInfo">
<div class="form-group">
<label>邮箱</label>
<input type="email" class="form-control" placeholder="Enter email" name="email" value="{$info.email}">
</div>
<div class="form-group">
<label>生日</label>
<input type="date" class="form-control" placeholder="出生日期" name="birthday" value="{$info.birthday}">
</div>
<div class="checkbox">
<label>
<input type="radio" value="1" name="sex" <eq name="info.sex" value="1">checked</eq>> 男
</label>
<label>
<input type="radio" value="0" name="sex" <eq name="info.sex" value="0">checked</eq>> 女
</label>
</div>
<div class="form-group">
<volist name="oauths" id="oauth">
<eq name="oauth.status" value="1">
<label for="">已经绑定{$oauth.title}</label> <a href="{:U('/user/unbindOauth','id='.$oauth['id'])}">取消绑定</a>
<else/>
<label for="">绑定{$oauth.title}</label> <a href="{:U('/user/oauth','type='.$oauth['type'])}">设置</a>
</eq>
<br>
</volist>
</div>
<div class="form-group">
<input type="file" id="upload_file" name="file" style="width:44px">
<input type="hidden" name="avatar" id="avatar" value="{$info.avatar|default=0}" />
<input type="hidden" name="id" value="{$info.id}">
</div>
<button type="submit" class="btn btn-primary ajax-put" target-form="MemberInfo">保存</button>
</form>
~~~
仍然用api put方式更新。
然后主要是头像上传后并支持了裁剪功能。
~~~
<script type="text/javascript">
var uploadify_swf = '__BOWER__/uploadify/uploadify.swf';
<present name="info">
var no_pic = '{$info.avatar|get_cover="path"}' || '__PUBLIC__/images/no-cover.png';
<else />
var no_pic = '__PUBLIC__/images/no-cover.png';
</present>
/* 初始化上传插件 */
$(function(){
$("#upload_file").uploadify({
"width" : 150,
"height" : 150,
"buttonImage" : no_pic,
"multi": false,
"swf" : uploadify_swf,
"fileObjName" : "file",
"buttonText" : "",
'buttonClass' : "btn_upload",
"formData": {
'session_id': '{:session_id()}',
'ajax': 1,
'model': 'Picture',
'field': 'file',
},
"uploader" : "{:U('Upload/ajaxUpload')}",
"removeCompleted": true,
"removeTimeout" : 1,
"fileSizeLimit": '5MB',
"fileTypeDesc": '图片',
"fileTypeExts" : '*.jpg; *.png; *.gif;',
"onFallback" : function() {
notify("未检测到兼容版本的Flash");
},
"onUploadSuccess" : afterUpload,
"onUploadError": function(file, errorCode, errorMsg, errorString) {
var msg = '<a title="' + errorString + '">上传失败<a/>';
notify(msg);
}
});
function afterUpload(file, data){
console.log(data);
var data = $.parseJSON(data);
var src = '';
if(data.status){
$("#avatar").val(data.id);
src = data.url || '__ROOT__' + data.path;
var $layer = $('#jcrop_layer');
$('#jcrop_img', $layer).html('<img src="'+src+'" id="jcrop_target"/>');
$('[name="file"]', $layer).val(src);
$layer.on('show.bs.modal', function (event) {
function updateView (c){
$('[name="width"]').val(c.w);
$('[name="height"]').val(c.h);
$('[name="x"]').val(c.x);
$('[name="y"]').val(c.y);
}
var model = $(this);
$('#jcrop_target').Jcrop({
minSize:[100,100],
allowSelect:false,
aspectRatio: 1,
bgColor: 'black',
bgOpacity: .4,
boxWidht: 400,
boxHeight: 400,
setSelect: [ 200, 200, 50, 50 ],
onSelect: updateView
});
$('.cropForm').on('submit', function(){
var $this = $(this);
$.ajax({
url: $this.attr('action'),
type: 'POST',
dataType: 'json',
data: $this.serialize(),
})
.done(function(data) {
console.log(data);
if(data.status){
notify(data.info, 'success');
$("#upload_file").uploadify('settings', 'buttonImage', data.path);
$("#avatar").val(data.id);
$layer.modal('hide');
}else{
notify(data.info, 'error');
}
})
.fail(function() {
console.log("error");
})
.always(function() {
console.log("complete");
});
return false;
});
});
$layer.on('hidden.bs.modal', function (event) {
$(this).removeData('bs.modal');
});
$layer.modal('show');
} else {
notify(data.info);
}
}
});
</script>
~~~
效果是:
![2015-06-28/558fee486df02](http://box.kancloud.cn/2015-06-28_558fee486df02.png)
![2015-06-28/558fee5f9c8b1](http://box.kancloud.cn/2015-06-28_558fee5f9c8b1.png)
主要是用了[jcrop插件](http://deepliquid.com/content/Jcrop_Manual.html)。
首先,uploadify 写了一个回调函数afterUpload。
然后回调函数里将上传完的图片替换 弹窗层里的img。
`$('#jcrop_img', $layer).html('<img src="'+src+'" id="jcrop_target"/>');`
在boostrap弹层 弹出事件里 初始化 裁剪插件:
~~~
function updateView (c){
$('[name="width"]').val(c.w);
$('[name="height"]').val(c.h);
$('[name="x"]').val(c.x);
$('[name="y"]').val(c.y);
}
var model = $(this);
$('#jcrop_target').Jcrop({
minSize:[100,100],
allowSelect:false,
aspectRatio: 1,
bgColor: 'black',
bgOpacity: .4,
boxWidht: 400,
boxHeight: 400,
setSelect: [ 200, 200, 50, 50 ],
onSelect: updateView
});
~~~
指定好各种参数。
~~~
<div id="jcrop_layer" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="gridSystemModalLabel">设置头像</h4>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8">
<div class="row center-block" id="jcrop_img"></div>
<div class="row">
图片大小不能超过5M,且只允许为png、gif、jpg格式图片
</div>
</div>
<div class="col-md-2"></div>
</div>
</div>
</div>
<div class="modal-footer">
<form action="{:U('/upload/crop')}" method="post" class="cropForm">
<input type="hidden" name="file">
<input type="hidden" name="width">
<input type="hidden" name="height">
<input type="hidden" name="x">
<input type="hidden" name="y">
<button type="submit" class="btn btn-primary">更新</button>
</form>
</div>
</div>
</div>
</div>
~~~
预览后,拖拽选区,选择要裁剪的 范围区域,最后交给裁剪表单处理,即向upload/crop提交 file(文件路径)、width(裁剪区域宽度)、height(裁剪区域高度)、x(裁剪区域x坐标)、y(裁剪区域y坐标)。
我们看上传控制器里的crop方法:
~~~
public function crop($file, $x, $y, $width, $height){
$image = new \Think\Image();
$image->open('.'.$file);
//将图片裁剪为$widthx$height并保存为$tmp_file
$tmp_file = date('Y_m_d_h_i_s').'.'.$image->type();
$local_file = './Uploads/tmp/'.$tmp_file;
$image->crop($width, $height,$x,$y)->save($local_file);
if(!is_file($local_file)){
$this->error('生成裁剪文件失败');
}
$res = local_upload($local_file, 'Picture');
if($res['status'])
$this->success('裁剪成功', '', $res);
else
$this->error($res['error']);
}
~~~
主要就是调用image类的crop方法。裁剪出新的临时图片,然后用local_upload函数重新插入到picture表里。然后前台替换头像id。
local_upload函数:
~~~
/**
* 网站上文件模拟上传,避免重复,且可以统一管理
* @param $local_file string 本地文件绝对路径,
* @param $model string Picture 或 File 走哪个模型 对应图片 和普通文件
* @param $save_path string 保存的目标路径,尽量和项目中2种文件一致,当然支持自定义
* @return array 成功返回array('status'=>1, 'id'=>1, 'path'=>'./Uploads/picture/2015_04_05_09_45_00.png') 类似地址, 失败status=0, error=失败原因
*/
function local_upload($local_file, $model = 'Pictrue', $saveroot = './Uploads/picture/'){
if (!$local_file)
return array('status'=>0, 'error'=>'文件路径为空');
$md5 = md5_file($local_file);
if($record = M($model)->where("md5 = '{$md5}'")->find()){
return array('status'=>1, 'id'=>$record['id'], 'path'=>$record['path']);
}
$upload_config = C(strtoupper($model).'_UPLOAD');
$ext_arr = explode(',', $upload_config['exts']);
$ext = strtolower(end(explode('.', $local_file)));
if (!in_array($ext, $ext_arr))
return array('status'=>0, 'error'=>"非法后缀{$ext}");
$filename = uniqid();
if($upload_config['autoSub']){
$rule = $upload_config['subName'];
$name = '';
if(is_array($rule)){ //数组规则
$func = $rule[0];
$param = (array)$rule[1];
foreach ($param as &$value) {
$value = str_replace('__FILE__', $filename, $value);
}
$name = call_user_func_array($func, $param);
} elseif (is_string($rule)){ //字符串规则
if(function_exists($rule)){
$name = call_user_func($rule);
} else {
$name = $rule;
}
}
$savepath .= $name.'/';
}
$savepath = $saveroot. $savepath;
if (!is_dir($savepath)){
if(!mkdir($savepath, 0777))
return array('status'=>0, 'error'=>"创建保存目录{$savepath}失败");
}
if (!is_readable($savepath)){
chmod($savepath, 0777);
}
$filename = $savepath . $filename . '.' . $ext;
ob_start();
readfile($local_file);
$file = ob_get_contents();
ob_end_clean();
$size = strlen($file);
slog($filename);
$fp2 = @fopen($filename, "a");
if(false === fwrite($fp2, $file)){
return array('status'=>0, 'error'=>'写入目标文件失败');
}
fclose($fp2);
$data = array(
'name' => end(explode('/', $local_file)),
'path' => str_replace('./', '/', $filename),
'md5' => $md5,
'sha1' => sha1($filename),
'status' => 1,
'create_time' => NOW_TIME,
);
$id = M($model)->add($data);
slog($local_file);
@unlink($local_file);
if(false === $id){
@unlink($filename);
return array('status'=>0, 'error'=>'保存上传文件记录失败');
}else{
return array('status'=>1, 'id'=>$id, 'path'=>$data['path']);
}
}
~~~
这个函数是之前有个用OneThink做的内部管理系统项目,有个要求采集原有系统,插入到OneThink开发的新系统文章库里。我通过采集文章内容,匹配第一个图片地址,后写的函数。为了符合onethink文件上传的不重复的管理风格。
![2015-06-28/558ff507f0d5f](http://box.kancloud.cn/2015-06-28_558ff507f0d5f.png)
裁剪完完后替换avatar表单项和将uploadify 插件的按钮背景设置为新图片。
可能裁剪的图片大于按钮背景大小。
css处理了下:
~~~
.uploadify-button{
background-size: cover;
}
~~~
- 序
- 前言
- 内容简介
- 目录
- 基础知识
- 起步
- 控制器
- 模型
- 模板
- 命名空间
- 进阶知识
- 路由
- 配置
- 缓存
- 权限
- 扩展
- 国际化
- 安全
- 单元测试
- 拿来主义
- 调试方法
- 调试的步骤
- 调试工具
- 显示trace信息
- 开启调试和关闭调试的区别
- netbeans+xdebug
- Socketlog
- PHP常见错误
- 小黄鸭调试法,每个程序员都要知道的
- 应用场景
- 第三方登录
- 图片处理
- 博客
- SAE
- REST实践
- Cli
- ajax分页
- barcode条形码
- excel
- 发邮件
- 汉字转全拼和首字母,支持带声调
- 中文分词
- 浏览器useragent解析
- freelog项目实战
- 需求分析
- 数据库设计
- 编码实践
- 前端实现
- rest接口
- 文章发布
- 文件上传
- 视频播放
- 音乐播放
- 图片幻灯片展示
- 注册和登录
- 个人资料更新
- 第三方登录的使用
- 后台
- 微信的开发
- 首页及个人主页
- 列表
- 归档
- 搜索
- 分页
- 总结经验
- 自我提升
- 进行小项目的锻炼
- 对现有轮子的重构和移植
- 写技术博客
- 制作视频教程
- 学习PHP的知识和新特性
- 和同行直接沟通、交流
- 学好英语,走向国际
- 如何参与
- 浏览官网和极思维还有看云
- 回答ThinkPHP新手的问题
- 尝试发现ThinkPHP的bug,告诉官方人员或者push request
- 开发能提高效率的ThinkPHP工具
- 尝试翻译官方文档
- 帮新手入门
- 创造基于ThinkPHP的产品,进行连带推广
- 展望未来
- OneThink
- ThinkPHP4
- 附录