增加文件夹分类功能,待完善.

This commit is contained in:
wispx
2018-12-27 18:07:23 +08:00
parent a2040c560e
commit b259352dee
15 changed files with 481 additions and 119 deletions
+17
View File
@@ -0,0 +1,17 @@
<?php
/**
* User: Wisp X
* Date: 2018-12-27
* Time: 11:08
* Link: https://github.com/wisp-x
*/
namespace app\common\model;
use think\Model;
use think\model\concern\SoftDelete;
class Folders extends Model
{
use SoftDelete;
}
+5
View File
@@ -75,4 +75,9 @@ class Users extends Model
{
return $this->hasMany('Images', 'user_id', 'id');
}
public function folders()
{
return $this->hasMany('Folders', 'user_id', 'id');
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* User: Wisp X
* Date: 2018-12-27
* Time: 14:30
* Link: https://github.com/wisp-x
*/
namespace app\common\validate;
use think\Validate;
class Folders extends Validate
{
protected $rule = [
'parent_id' => 'require|number|integer',
'name' => 'require|max:30|chsAlphaNum'
];
protected $message = [
'parent_id.require' => '没有找到上级文件夹',
'parent_id.number' => '上级文件夹异常',
'parent_id.integer' => '上级文件夹异常',
'name.require' => '文件夹名称不能为空',
'name.max' => '文件夹名称长度最大30个字符',
'name.chsAlphaNum' => '文件夹名称只能是汉字、字母和数字'
];
}
+8
View File
@@ -190,6 +190,14 @@ EOT;
if ($start == 1) {
if ($file) {
$config = Config::pull('db');
// 替换sql关键字
$file = str_replace([
'{database}'
], [
$config['database']
], $file);
$mysqli = new \mysqli(
$config['hostname'],
$config['username'],
+94 -2
View File
@@ -8,6 +8,7 @@
namespace app\index\controller;
use app\common\model\Folders;
use app\common\model\Images;
use think\Db;
use think\facade\Config;
@@ -16,14 +17,18 @@ use think\Exception;
class User extends Base
{
public function images($keyword = '', $limit = 60)
public function images($keyword = '', $folderId = 0, $limit = 60)
{
if ($this->request->isPost()) {
try {
$model = $this->user->images()->order('create_time', 'desc');
$folders = $this->user->folders()->where('parent_id', $folderId)->select();
if (!empty($keyword)) {
$model = $model->where('pathname', 'like', "%{$keyword}%");
}
if (is_numeric($folderId)) {
$model = $model->where('folder_id', $folderId);
}
$images = $model->paginate($limit)->each(function ($item) {
$item->url = $item->url;
// TODO 生成缩略图
@@ -32,7 +37,10 @@ class User extends Base
} catch (Exception $e) {
return $this->error($e->getMessage());
}
return $this->success('success', null, $images);
return $this->success('success', null, [
'images' => $images,
'folders'=> $folders
]);
}
return $this->fetch();
}
@@ -90,6 +98,90 @@ class User extends Base
}
}
public function createFolder()
{
if ($this->request->isPost()) {
try {
$parentId = $this->request->post('parent_id');
$name = $this->request->post('name');
$data = [
'user_id' => $this->user->id,
'parent_id' => $parentId,
'name' => $name
];
$validate = $this->validate($data, 'Folders');
if (true !== $validate) {
throw new Exception($validate);
}
Folders::create($data);
} catch (Exception $e) {
return $this->error($e->getMessage());
}
return $this->success('创建成功');
}
}
public function deleteFolder()
{
if ($this->request->isPost()) {
Db::startTrans();
try {
$id = $this->request->post('id');
$folders = $images = [];
$this->getDeleteFoldersAndImages($id, $folders, $images);
$folders[] = (int) $id;
Folders::destroy($folders, true);
Images::destroy($images, true);
Db::commit();
} catch (Exception $e) {
Db::rollback();
return $this->error($e->getMessage());
}
return $this->success('删除成功');
}
}
public function renameFolder()
{
if ($this->request->isPost()) {
Db::startTrans();
try {
$id = $this->request->post('id');
$parentId = $this->request->post('parent_id');
$name = $this->request->post('name');
$data = [
'id' => $id,
'parent_id' => $parentId,
'user_id' => $this->user->id,
'name' => $name
];
$validate = $this->validate($data, 'Folders');
if (true !== $validate) {
throw new Exception($validate);
}
Folders::update($data);
Db::commit();
} catch (Exception $e) {
Db::rollback();
return $this->error($e->getMessage());
}
return $this->success('重命名成功');
}
}
private function getDeleteFoldersAndImages($folderId, &$folders, &$images)
{
$folderList = Folders::where('parent_id', $folderId)->column('id');
$imagesList = Images::where('folder_id', $folderId)->column('id');
if ($imagesList) {
$images = array_merge($images, $imagesList);
}
foreach ($folderList as &$value) {
$folders[] = $value;
$this->getDeleteFoldersAndImages($value, $folders, $images);
}
}
public function settings()
{
if ($this->request->isPost()) {
+2 -2
View File
@@ -13,7 +13,7 @@
<link rel="shortcut icon" href="/favicon.ico">
<link rel="stylesheet" href="/static/app/iconfont/iconfont.css">
<link rel="stylesheet" href="/static/mdui/0.4.1/css/mdui.min.css">
<link rel="stylesheet" href="/static/app/css/app.css">
<link rel="stylesheet" href="/static/app/css/app.css?v=1.0">
<!--[if IE]>
<script>window.location.href = '/compatibility.html';</script>
<![endif]-->
@@ -139,7 +139,7 @@
<script src="/static/jquery/3.3.1/jquery.min.js"></script>
<script src="/static/mdui/0.4.1/js/mdui.min.js"></script>
<script src="/static/marked/0.5.1/marked.js"></script>
<script src="/static/app/js/app.js"></script>
<script src="/static/app/js/app.js?v=1.0"></script>
{block name="js"}{/block}
<script>
var thatVer = '{$config.system_version}';
+181 -59
View File
@@ -3,8 +3,8 @@
{block name="title"}图片管理 - {$config.site_name}{/block}
{block name="css"}
<link rel="stylesheet" href="/static/contextjs/css/context.standalone.css">
<link rel="stylesheet" href="/static/jquery-viewer/1.2.0/css/viewer.css">
<link rel="stylesheet" href="/static/contextjs/css/context.standalone.css">
<link rel="stylesheet" href="/static/jquery-viewer/1.2.0/css/viewer.css">
{/block}
{block name="main"}
@@ -30,9 +30,15 @@
</form>
<div class="mdui-clearfix"></div>
</div>
<ul class="breadcrumb">
<li class="active"><a data-id="0">Home</a></li>
</ul>
<div class="mdui-divider mdui-m-b-1"></div>
<div class="mdui-row">
<div id="images-box" class="images-box mdui-row-xs-3 mdui-row-sm-9 mdui-row-md-9 mdui-row-lg-10 mdui-col-xl-12"></div>
<div class="box mdui-row-xs-3 mdui-row-sm-9 mdui-row-md-9 mdui-row-lg-10 mdui-col-xl-12">
<div class="folders-box"></div>
<div class="images-box"></div>
</div>
</div>
<button class="mdui-btn mdui-ripple mdui-center mdui-color-grey-300 mdui-text-color-black-secondary mdui-m-t-2 more"></button>
<!-- Info Dialog -->
@@ -97,31 +103,47 @@
<script>
$(function () {
var params = {}, viewer, imagesBox = $('.images-box'), totalBox = $('small.num'), infoDialog = $(".mdui-dialog#info"), qrcodeApi = 'https://www.kuaizhan.com/common/encode-png?large=true&data=';
var params = {}, viewer, foldersBox = $('.folders-box'), imagesBox = $('.images-box'), totalBox = $('small.num'), infoDialog = $(".mdui-dialog#info"), qrcodeApi = 'https://www.kuaizhan.com/common/encode-png?large=true&data=';
params.page = 1;
// 函数库
var methods = {
getImages: function () {
getData: function (options) {
params = options || params;
var more = $(".more");
more.attr('disabled', true).text('加载中...');
app.ajax("{:url('user/images')}", params, function (response) {
if (response.code) {
var list = response.data.data;
totalBox.text(response.data.total);
if (list.length) {
for (var i = 0; i < list.length; i++) {
var images = response.data.images.data;
var folders = response.data.folders;
totalBox.text(response.data.images.total);
if (folders.length) {
for (var f = 0; f < folders.length; f++) {
foldersBox.append(
' <div class="mdui-col">' +
' <div class="item" data-id="' + folders[f].id + '" data-parent-id="' + folders[f].parent_id + '" data-name="' + folders[f].name + '">' +
' <div class="info">' +
' <img src="/static/app/images/folder.svg">' +
' </div>' +
' <p class="name">' + folders[f].name + '</p>' +
' </div>' +
'</div>'
);
}
}
if (images.length) {
for (var i = 0; i < images.length; i++) {
imagesBox.append(
'<div class="mdui-col">' +
' <div data-id="' + list[i].id + '" data-json=\'' + JSON.stringify(list[i]) + '\' class="item" title=' + list[i].name + '>' +
' <i class="choice iconfont icon-choice"></i>' +
' <i class="info iconfont icon-info"></i>' +
' <div class="info image">' +
' <img data-original="' + list[i].url + '" src="' + list[i].url + '">' +
' </div>' +
' <p class="name">' + list[i].name + '</p>' +
' </div>' +
' </div>');
' <div class="item" data-id="' + images[i].id + '" data-json=\'' + JSON.stringify(images[i]) + '\' title=' + images[i].name + '>' +
' <i class="choice iconfont icon-choice"></i>' +
' <i class="info iconfont icon-info"></i>' +
' <div class="info image">' +
' <img data-original="' + images[i].url + '" src="' + images[i].url + '">' +
' </div>' +
' <p class="name">' + images[i].name + '</p>' +
' </div>' +
'</div>');
}
// 图片预览插件
@@ -134,17 +156,22 @@
viewer.data('viewer').update();
}
if (response.data.current_page == response.data.last_page) {
if (response.data.images.current_page == response.data.images.last_page) {
return more.attr('disabled', true).text('我也是有底线的~');
}
params.page++;
return more.attr('disabled', false).text('加载更多');
} else {
more.attr('disabled', true).text('暂无数据');
more.attr('disabled', true).text('暂无图片');
}
}
});
},
resetData: function (options) {
foldersBox.html('');
imagesBox.html('');
methods.getData(options);
},
// 删除
delete: function (id, batch, callback) {
var msg = '确认删除该图片吗?';
@@ -161,7 +188,7 @@
}
};
methods.getImages();
methods.getData();
// 监听复制操作
var clipboard = new ClipboardJS('.copy-url');
@@ -179,50 +206,149 @@
// Context Start
context.init({
fadeSpeed: 100,
filter: function ($obj){},
filter: function ($obj) {},
above: 'auto',
preventDoubleContext: true,
compress: false
});
$('body').on('contextmenu', '.images-box .item', function(e) {
var item = $(e.target).closest('.item'), data = item.data('json');
context.attach('.images-box .item', [
{header: 'Compressed Menu'},
{text: '查看图片', action: function (e) {
// 右键操作
$('.images-container').on('contextmenu', '.box', function(e) {
var $that = $(e.target), $item = $that.closest('.item');
if ($that.parents().hasClass('folders-box')) {
context.attach('.folders-box .item', [
{header: 'Compressed Menu'},
{text: '打开', action: function (e) {
$item.click();
}
},
{text: '重新命名', action: function (e) {
e.preventDefault();
item.find('img').click();
mdui.prompt('请输入文件夹名称',
function (value) {
app.ajax("{:url('user/renameFolder')}", {
id: $item.data('id'),
parent_id: $item.data('parent-id'),
name: value
}, function (response) {
if (response.code) {
$item.attr('data-name', value).find('p.name').text(value);
return app.msg(true, response.msg);
}
mdui.alert(response.msg);
})
}
);
}
},
{divider: true},
{text: '删除', action: function (e) {
e.preventDefault();
mdui.confirm('确认删除该文件夹吗?', function() {
app.ajax("{:url('user/deleteFolder')}", {id: $item.data('id')}, function (response) {
if (response.code) {
$item.parent().remove();
return app.msg(true, response.msg);
}
mdui.alert(response.msg);
})
});
}
},
]);
} else if ($that.parents().hasClass('images-box')) {
var $item = $that.closest('.item'), data = $item.data('json');
context.attach('.images-box .item', [
{header: 'Compressed Menu'},
{text: '查看图片', action: function (e) {
e.preventDefault();
$item.find('img').click();
}
},
{text: '新窗口打开图片', href: data.url, target: '_blank'},
{divider: true},
{text: '复制链接', action: function (e) {
e.preventDefault();
$('#copy-url').attr('data-clipboard-text', data.url).click();
}
},
{
text: '删除', action: function (e) {
e.preventDefault();
methods.delete(data.id, false, function () {
$item.parent().remove();
totalBox.text(parseInt(totalBox.text()) - 1);
})
}
},
{
text: '属性', action: function (e) {
e.preventDefault();
$item.find('i.info').click();
}
}
},
{text: '新窗口打开图片', href: data.url, target: '_blank'},
{divider: true},
{text: '复制链接', action: function (e) {
]);
} else {
context.attach('.box', [
{header: 'Compressed Menu'},
{text: '新建文件夹', action: function (e) {
e.preventDefault();
$('#copy-url').attr('data-clipboard-text', data.url).click();
}
},
{
text: '删除', action: function (e) {
e.preventDefault();
methods.delete(data.id, false, function () {
item.parent().remove();
totalBox.text(parseInt(totalBox.text()) - 1);
})
}
},
{
text: '属性', action: function (e) {
e.preventDefault();
item.find('i.info').click();
}
}
]);
mdui.prompt('请输入文件夹名称',
function (value) {
app.ajax("{:url('user/createFolder')}", {
parent_id: $('ul.breadcrumb li.active:last-child a').data('id'),
name: value
}, function (response) {
if (response.code) {
methods.resetData();
return app.msg(true, response.msg);
}
mdui.alert(response.msg);
})
}
);
}
},
{divider: true},
{text: '刷新', action: function (e) {
// e.preventDefault();
methods.resetData();
}
},
]);
}
});
// 文件夹点击操作
$('.folders-box').on('click', '', function (e) {
var $that = $(e.target), $item = $that.closest('.item');
$('ul.breadcrumb li:last-child').removeClass('active');
$('ul.breadcrumb').append(
'<li class="active">' +
' <a data-id="' + $item.data('id') + '">' + $item.data('name') + '</a>' +
'</li>'
);
methods.resetData({
page: 1,
folderId: $item.data('id')
});
});
// 面包屑导航点击
$('ul.breadcrumb').on('click', 'li:not(.active) a', function (e) {
e.preventDefault();
methods.resetData({
page: 1,
folderId: $(this).data('id')
});
$(this).parent('li').addClass('active').nextAll().remove();
});
// Context End
// 加载更多
$('.more').click(function () {
methods.getImages();
methods.getData();
});
// 筛选
@@ -231,7 +357,7 @@
// 清空内容
imagesBox.html('');
params.keyword = $(this).find("input[name='keyword']").val();
methods.getImages();
methods.getData();
});
// 选中项操作
@@ -247,7 +373,7 @@
methods.delete(array, true, function () {
selected.remove();
totalBox.text(parseInt(totalBox.text()) - selected.length);
// methods.getImages();
// methods.getData();
});
}
} else {
@@ -332,11 +458,7 @@
// 重置数据
$('#reset').click(function () {
params = {
page: 1
};
imagesBox.html('');
methods.getImages();
methods.resetData();
});
})
</script>