Forráskód Böngészése

新增附件分类功能
新增上传接口设定分类功能
优化分片上传进行百分比计算
优化系统配置字典文字显示

Karson 4 éve
szülő
commit
bf720c176e

+ 3 - 1
application/admin/command/Install/fastadmin.sql

@@ -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;
 
 -- ----------------------------

+ 35 - 4
application/admin/controller/general/Attachment.php

@@ -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();
+    }
+
 }

+ 8 - 0
application/admin/controller/general/Config.php

@@ -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;
         }

+ 8 - 0
application/admin/lang/zh-cn/general/attachment.php

@@ -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'       => '从编辑器上传'
 ];

+ 3 - 0
application/admin/lang/zh-cn/general/config.php

@@ -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'                   => '邮件发送方式',

+ 13 - 0
application/admin/view/general/attachment/edit.html

@@ -1,6 +1,19 @@
+
+
 <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">
             <input type="text" name="row[url]" value="{$row.url|htmlentities}"  id="c-url" class="form-control" required />

+ 16 - 2
application/admin/view/general/attachment/index.html

@@ -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>

+ 2 - 4
application/admin/view/general/attachment/select.html

@@ -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">

+ 3 - 0
application/common/library/Upload.php

@@ -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'],

+ 24 - 0
application/common/model/Attachment.php

@@ -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()
     {
         // 如果已经上传该资源,则不再记录

+ 5 - 0
application/extra/site.php

@@ -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',

+ 33 - 8
public/assets/js/backend/general/attachment.js

@@ -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>';
                     }

+ 10 - 4
public/assets/js/require-upload.js

@@ -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;