新增附件分类功能

新增上传接口设定分类功能
优化分片上传进行百分比计算
优化系统配置字典文字显示
pull/305/MERGE
Karson 2021-06-04 14:33:44 +08:00
parent 7045f0d02b
commit bf720c176e
13 changed files with 163 additions and 23 deletions

View File

@ -80,6 +80,7 @@ CREATE TABLE `fa_area` (
DROP TABLE IF EXISTS `fa_attachment`;
CREATE TABLE `fa_attachment` (
`id` int(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`category` varchar(50) DEFAULT '' COMMENT '类别',
`admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID',
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID',
`url` varchar(255) DEFAULT '' COMMENT '物理路径',
@ -103,7 +104,7 @@ CREATE TABLE `fa_attachment` (
-- Records of fa_attachment
-- ----------------------------
BEGIN;
INSERT INTO `fa_attachment` VALUES (1, 1, 0, '/assets/img/qrcode.png', '150', '150', 'png', 0, 'qrcode.png', 21859, 'image/png', '', 1491635035, 1491635035, 1491635035, 'local', '17163603d0263e4838b9387ff2cd4877e8b018f6');
INSERT INTO `fa_attachment` VALUES (1, '', 1, 0, '/assets/img/qrcode.png', '150', '150', 'png', 0, 'qrcode.png', 21859, 'image/png', '', 1491635035, 1491635035, 1491635035, 'local', '17163603d0263e4838b9387ff2cd4877e8b018f6');
COMMIT;
-- ----------------------------
@ -350,6 +351,7 @@ INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user',
INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码或授权码)', 'string', 'password', '', '', '', '');
INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', 'SMTP验证方式[推荐SSL]', 'select', '2', '[\"无\",\"TLS\",\"SSL\"]', '', '', '');
INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '10000@qq.com', '', '', '', '');
INSERT INTO `fa_config` VALUES (18, 'attachmentcategory', 'dictionary', 'Attachment category', '', 'array', '{\"category1\":\"Category1\",\"category2\":\"Category2\",\"custom\":\"Custom\"}', '', '', '', '');
COMMIT;
-- ----------------------------

View File

@ -8,7 +8,7 @@ use app\common\controller\Backend;
* 附件管理
*
* @icon fa fa-circle-o
* @remark 主要用于管理上传到又拍云的数据或上传至本服务的上传数据
* @remark 主要用于管理上传到服务器或第三方存储的数据
*/
class Attachment extends Backend
{
@ -18,11 +18,15 @@ class Attachment extends Backend
*/
protected $model = null;
protected $searchFields = 'id,filename,url';
public function _initialize()
{
parent::_initialize();
$this->model = model('Attachment');
$this->view->assign("mimetypeList", \app\common\model\Attachment::getMimetypeList());
$this->view->assign("categoryList", \app\common\model\Attachment::getCategoryList());
$this->assignconfig("categoryList", \app\common\model\Attachment::getCategoryList());
}
/**
@ -36,10 +40,15 @@ class Attachment extends Backend
$mimetypeQuery = [];
$filter = $this->request->request('filter');
$filterArr = (array)json_decode($filter, true);
if (isset($filterArr['category']) && $filterArr['category'] == 'unclassed') {
$filterArr['category'] = '';
$this->request->get(['filter' => json_encode(array_diff_key($filterArr, ['category' => '']))]);
}
if (isset($filterArr['mimetype']) && preg_match("/[]\,|\*]/", $filterArr['mimetype'])) {
$this->request->get(['filter' => json_encode(array_diff_key($filterArr, ['mimetype' => '']))]);
$mimetypeQuery = function ($query) use ($filterArr) {
$mimetypeArr = explode(',', $filterArr['mimetype']);
$mimetype = $filterArr['mimetype'];
$filterArr = array_diff_key($filterArr, ['mimetype' => '']);
$mimetypeQuery = function ($query) use ($mimetype) {
$mimetypeArr = explode(',', $mimetype);
foreach ($mimetypeArr as $index => $item) {
if (stripos($item, "/*") !== false) {
$query->whereOr('mimetype', 'like', str_replace("/*", "/", $item) . '%');
@ -49,6 +58,7 @@ class Attachment extends Backend
}
};
}
$this->request->get(['filter' => json_encode($filterArr)]);
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
@ -121,4 +131,25 @@ class Attachment extends Backend
$this->error(__('Parameter %s can not be empty', 'ids'));
}
/**
* 移动
*/
public function move($ids = "")
{
if (!$this->request->isPost()) {
$this->error(__("Invalid parameters"));
}
$category = $this->request->post('category', '');
$ids = $this->request->post('ids');
if (!$ids) {
$this->error(__('Parameter %s can not be empty', 'ids'));
}
$categoryList = \app\common\model\Attachment::getCategoryList();
if ($category && !isset($categoryList[$category])) {
$this->error(__('Category not found'));
}
\app\common\model\Attachment::where('id', 'in', $ids)->update(['category' => $category]);
$this->success();
}
}

View File

@ -60,6 +60,14 @@ class Config extends Backend
$value['value'] = explode(',', $value['value']);
}
$value['content'] = json_decode($value['content'], true);
if (in_array($value['name'], ['categorytype', 'configgroup', 'attachmentcategory'])) {
$dictValue = (array)json_decode($value['value'], true);
foreach ($dictValue as $index => &$item) {
$item = __($item);
}
unset($item);
$value['value'] = json_encode($dictValue, JSON_UNESCAPED_UNICODE);
}
$value['tip'] = htmlspecialchars($value['tip']);
$siteList[$v['group']]['list'][] = $value;
}

View File

@ -23,9 +23,17 @@ return [
'Createtime' => '创建日期',
'Uploadtime' => '上传时间',
'Storage' => '存储引擎',
'Category1' => '分类一',
'Category2' => '分类二',
'Custom' => '自定义',
'Unclassed' => '未归类',
'Category' => '类别',
'Upload to third' => '上传到第三方',
'Upload to local' => '上传到本地',
'Upload to third by chunk' => '上传到第三方(分片模式)',
'Upload to local by chunk' => '上传到本地(分片模式)',
'Please enter a new name' => '请输入新的类别名称',
'Please select category' => '请选择一个类别',
'Category not found' => '指定的类别未找到',
'Upload from editor' => '从编辑器上传'
];

View File

@ -57,6 +57,9 @@ return [
'Fixed page' => '后台固定页',
'Category type' => '分类类型',
'Config group' => '配置分组',
'Attachment category' => '附件类别',
'Category1' => '分类一',
'Category2' => '分类二',
'Rule tips' => '校验规则使用请参考Nice-validator文档',
'Extend tips' => '扩展属性支持{id}、{name}、{group}、{title}、{value}、{content}、{rule}替换',
'Mail type' => '邮件发送方式',

View File

@ -1,5 +1,18 @@
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="c-url" class="control-label col-xs-12 col-sm-2">{:__('Category')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[category]" class="form-control">
<option value="">{:__('Please select category')}</option>
{foreach name="categoryList" id="item"}
<option value="{$key}" {if $key==$row.category}selected{/if}>{$item}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label for="c-url" class="control-label col-xs-12 col-sm-2">{:__('Url')}:</label>
<div class="col-xs-12 col-sm-8">

View File

@ -2,9 +2,9 @@
<div class="panel-heading">
{:build_heading(null,FALSE)}
<ul class="nav nav-tabs" data-field="mimetype">
<ul class="nav nav-tabs" data-field="category">
<li class="active"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
{foreach name="mimetypeList" item="vo"}
{foreach name="categoryList" item="vo"}
<li><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
{/foreach}
</ul>
@ -16,6 +16,7 @@
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar('refresh,add,edit,del')}
<a class="btn btn-info btn-move dropdown-toggle btn-disabled disabled"><i class="fa fa-arrow-right"></i> {:__('Move')}</a>
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('general/attachment/edit')}"
@ -28,3 +29,16 @@
</div>
</div>
</div>
<script id="typetpl" type="text/html">
<div class="row">
<div class="col-xs-12">
<select name="category" class="form-control">
<option value="">{:__('Please select category')}</option>
{foreach name="categoryList" id="item"}
<option value="{$key}">{$item}</option>
{/foreach}
</select>
</div>
</div>
</script>

View File

@ -6,17 +6,15 @@
</style>
{/if}
<div class="panel panel-default panel-intro">
{if !$Think.get.mimetype}
<div class="panel-heading">
{:build_heading(null,FALSE)}
<ul class="nav nav-tabs" data-field="mimetype">
<ul class="nav nav-tabs" data-field="category">
<li class="active"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
{foreach name="mimetypeList" item="vo"}
{foreach name="categoryList" item="vo"}
<li><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
{/foreach}
</ul>
</div>
{/if}
<div class="panel-body no-padding">
<div id="myTabContent" class="tab-content">

View File

@ -352,10 +352,13 @@ class Upload
}
}
$this->file = $file;
$category = request()->post('category');
$category = array_key_exists($category, config('site.attachmentcategory') ?? []) ? $category : '';
$params = array(
'admin_id' => (int)session('admin.id'),
'user_id' => (int)cookie('uid'),
'filename' => substr(htmlspecialchars(strip_tags($this->fileInfo['name'])), 0, 100),
'category' => $category,
'filesize' => $this->fileInfo['size'],
'imagewidth' => $this->fileInfo['imagewidth'],
'imageheight' => $this->fileInfo['imageheight'],

View File

@ -24,6 +24,16 @@ class Attachment extends Model
return is_numeric($value) ? $value : strtotime($value);
}
public function getCategoryAttr($value)
{
return $value == '' ? 'unclassed' : $value;
}
public function setCategoryAttr($value)
{
return $value == 'unclassed' ? '' : $value;
}
/**
* 获取云储存的缩略图样式字符
*/
@ -53,6 +63,20 @@ class Attachment extends Model
return $data;
}
/**
* 获取定义的附件类别列表
* @return array
*/
public static function getCategoryList()
{
$data = config('site.attachmentcategory')??[];
foreach ($data as $index => &$datum) {
$datum = __($datum);
}
$data['unclassed'] = __('Unclassed');
return $data;
}
protected static function init()
{
// 如果已经上传该资源,则不再记录

View File

@ -25,6 +25,11 @@ return [
'user' => 'User',
'example' => 'Example',
],
'attachmentcategory' => [
'category1' => 'Category1',
'category2' => 'Category2',
'custom' => 'Custom',
],
'mail_type' => '1',
'mail_smtp_host' => 'smtp.qq.com',
'mail_smtp_port' => '465',

View File

@ -24,11 +24,12 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
[
{field: 'state', checkbox: true},
{field: 'id', title: __('Id')},
{field: 'category', title: __('Category'), formatter: Table.api.formatter.label, searchList: Config.categoryList},
{field: 'admin_id', title: __('Admin_id'), visible: false, addClass: "selectpage", extend: "data-source='auth/admin/index' data-field='nickname'"},
{field: 'user_id', title: __('User_id'), visible: false, addClass: "selectpage", extend: "data-source='user/user/index' data-field='nickname'"},
{field: 'preview', title: __('Preview'), formatter: Controller.api.formatter.thumb, operate: false},
{field: 'url', title: __('Url'), formatter: Controller.api.formatter.url, visible: false},
{field: 'filename', title: __('Filename'), formatter: Controller.api.formatter.filename, operate: 'like'},
{field: 'filename', title: __('Filename'), sortable: true, formatter: Controller.api.formatter.filename, operate: 'like'},
{
field: 'filesize', title: __('Filesize'), operate: 'BETWEEN', sortable: true, formatter: function (value, row, index) {
var size = parseFloat(value);
@ -38,7 +39,7 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
},
{field: 'imagewidth', title: __('Imagewidth'), sortable: true},
{field: 'imageheight', title: __('Imageheight'), sortable: true},
{field: 'imagetype', title: __('Imagetype'), formatter: Table.api.formatter.search, operate: 'like'},
{field: 'imagetype', title: __('Imagetype'), sortable: true, formatter: Table.api.formatter.search, operate: 'like'},
{field: 'storage', title: __('Storage'), formatter: Table.api.formatter.search, operate: 'like'},
{field: 'mimetype', title: __('Mimetype'), formatter: Table.api.formatter.search},
{
@ -47,7 +48,8 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
formatter: Table.api.formatter.datetime,
operate: 'RANGE',
addclass: 'datetimerange',
sortable: true
sortable: true,
width: 150
},
{
field: 'operate',
@ -63,6 +65,28 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
// 为表格绑定事件
Table.api.bindevent(table);
$(document).on('click', '.btn-move', function () {
var ids = Table.api.selectedids(table);
Layer.open({
title: __('Move'),
content: Template("typetpl", {}),
btn: [__('Move')],
yes: function (index, layero) {
var category = $("select[name='category']", layero).val();
Fast.api.ajax({
url: "general/attachment/move",
type: "post",
data: {category: category, ids: ids.join(',')},
}, function () {
table.bootstrapTable('refresh', {});
Layer.close(index);
});
},
success: function (layero, index) {
}
});
});
},
select: function () {
// 初始化表格参数配置
@ -106,14 +130,15 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
[
{field: 'state', checkbox: multiple, visible: multiple, operate: false},
{field: 'id', title: __('Id')},
{field: 'category', title: __('Category'), formatter: Table.api.formatter.label, searchList: Config.categoryList},
{field: 'admin_id', title: __('Admin_id'), formatter: Table.api.formatter.search, visible: false},
{field: 'user_id', title: __('User_id'), formatter: Table.api.formatter.search, visible: false},
{field: 'url', title: __('Preview'), formatter: Controller.api.formatter.thumb, operate: false},
{field: 'filename', title: __('Filename'), formatter: Controller.api.formatter.filename, operate: 'like'},
{field: 'imagewidth', title: __('Imagewidth'), operate: false},
{field: 'imageheight', title: __('Imageheight'), operate: false},
{field: 'filename', title: __('Filename'), sortable: true, formatter: Controller.api.formatter.filename, operate: 'like'},
{field: 'imagewidth', title: __('Imagewidth'), operate: false, sortable: true},
{field: 'imageheight', title: __('Imageheight'), operate: false, sortable: true},
{
field: 'mimetype', title: __('Mimetype'), operate: 'LIKE %...%',
field: 'mimetype', title: __('Mimetype'), sortable: true, operate: 'LIKE %...%',
process: function (value, arg) {
return value.replace(/\*/g, '%');
}
@ -162,7 +187,7 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
formatter: {
thumb: function (value, row, index) {
if (row.mimetype.indexOf("image") > -1) {
return '<a href="' + row.fullurl + '" target="_blank"><img src="' + row.fullurl + row.thumb_style + '" alt="" style="max-height:90px;max-width:120px"></a>';
return '<a href="' + row.fullurl + '" target="_blank"><img src="' + row.fullurl + row.thumb_style + '" alt="" style="max-height:60px;max-width:120px"></a>';
} else {
return '<a href="' + row.fullurl + '" target="_blank"><img src="' + Fast.api.fixurl("ajax/icon") + "?suffix=" + row.imagetype + '" alt="" style="max-height:90px;max-width:120px"></a>';
}

View File

@ -251,10 +251,16 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
Upload.events.onUploadError(this, ret, file);
},
uploadprogress: function (file, progress, bytesSent) {
if (file.upload.chunked) {
var totalBytesSent = 0;
file.upload.chunks.forEach(function (item) {
totalBytesSent += item.bytesSent;
});
$(this.element).prop("disabled", true).html("<i class='fa fa-upload'></i> " + __('Upload') + Math.floor((totalBytesSent / file.size) * 100) + "%");
}
},
totaluploadprogress: function (progress, bytesSent) {
if (this.getActiveFiles().length > 0) {
if (this.getActiveFiles().length > 0 && !this.options.chunking) {
$(this.element).prop("disabled", true).html("<i class='fa fa-upload'></i> " + __('Upload') + Math.floor(progress) + "%");
}
},
@ -269,13 +275,13 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
var that = this;
Fast.api.ajax({
url: this.options.url,
data: {
data: $.extend({}, multipart, {
action: 'merge',
filesize: file.size,
filename: file.name,
chunkid: file.upload.uuid,
chunkcount: file.upload.totalChunkCount,
}
})
}, function (data, ret) {
done(JSON.stringify(ret));
return false;