💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
个人资料的访问地址是: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">&times;</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; } ~~~