diff --git a/application/admin/command/Addon.php b/application/admin/command/Addon.php index aaba93a843378d0a2b04def04ba40bb44ef5d669..e60d5b6c328bfa64389ca1fad1fb881c935d1d49 100644 --- a/application/admin/command/Addon.php +++ b/application/admin/command/Addon.php @@ -95,6 +95,7 @@ class Addon extends Command ]; $this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php'); $this->writeToFile("config", $data, $addonDir . 'config.php'); + $this->writeToFile("configHtml", $data, $addonDir . 'config.html'); $this->writeToFile("info", $data, $addonDir . 'info.ini'); $this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php'); if ($createTableSql) { diff --git a/application/admin/command/Addon/stubs/addon.stub b/application/admin/command/Addon/stubs/addon.stub index 824e02c1c42f6f6de1d40dd16808676edb7fe2ae..c033d7917bda0dda9f5f193ae9388dec9f4c3535 100644 --- a/application/admin/command/Addon/stubs/addon.stub +++ b/application/admin/command/Addon/stubs/addon.stub @@ -10,6 +10,8 @@ use think\Addons; */ class {%addonClassName%} extends Addons { + // 启用自定义插件配置页 + public $configView = false; /** * 插件安装方法 diff --git a/application/admin/command/Addon/stubs/configHtml.stub b/application/admin/command/Addon/stubs/configHtml.stub new file mode 100644 index 0000000000000000000000000000000000000000..aa134d05d5d00ca092819eec6a2a1790ab7ed2fa --- /dev/null +++ b/application/admin/command/Addon/stubs/configHtml.stub @@ -0,0 +1,22 @@ + + + + + {%name%} + + + + + + + + +

{%name%}自定义配置页面

+ + + \ No newline at end of file diff --git a/application/admin/command/Addon/stubs/controller.stub b/application/admin/command/Addon/stubs/controller.stub index d79476f2578778a1e22159c2c1da053458022165..f1a5f80646c1b327b7ded126c0be4e6c04ccfe60 100644 --- a/application/admin/command/Addon/stubs/controller.stub +++ b/application/admin/command/Addon/stubs/controller.stub @@ -3,13 +3,39 @@ namespace addons\{%addon%}\controller; use think\addons\Controller; +use think\Lang; class Index extends Controller { + protected function _initialize() + { + parent::_initialize(); + + $this->view->assign('info', get_addon_info($this->addon)); + $this->view->assign('lang', strip_tags($this->request->langset())); + } + + public function lang() + { + header('Content-Type: application/javascript'); + header("Cache-Control: public"); + header("Pragma: cache"); + + $offset = 30 * 60 * 60 * 24; // 缓存一个月 + header("Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT"); + + //默认只加载了控制器对应的语言名,你还根据控制器名来加载额外的语言包 + return jsonp(Lang::get(), 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]); + } public function index() { $this->error("当前插件暂无前台页面"); } + public function config() + { + return $this->view->fetch(ADDON_PATH . $this->addon . DS . 'config.html'); + } + } diff --git a/application/admin/command/Min.php b/application/admin/command/Min.php index afc81646aea0fd121422ec4f135d7b0b30b992a9..dd8603123a47e642f1c0cfcadd0d3e665a552a16 100644 --- a/application/admin/command/Min.php +++ b/application/admin/command/Min.php @@ -25,7 +25,7 @@ class Min extends Command { $this ->setName('min') - ->addOption('module', 'm', Option::VALUE_REQUIRED, 'module name(frontend or backend),use \'all\' when build all modules', null) + ->addOption('module', 'm', Option::VALUE_REQUIRED, 'module name(frontend or backend or addons),use \'all\' when build all modules', null) ->addOption('resource', 'r', Option::VALUE_REQUIRED, 'resource name(js or css),use \'all\' when build all resources', null) ->addOption('optimize', 'o', Option::VALUE_OPTIONAL, 'optimize type(uglify|closure|none)', 'none') ->setDescription('Compress js and css file'); @@ -37,14 +37,14 @@ class Min extends Command $resource = $input->getOption('resource') ?: ''; $optimize = $input->getOption('optimize') ?: 'none'; - if (!$module || !in_array($module, ['frontend', 'backend', 'all'])) { + if (!$module || !in_array($module, ['frontend', 'backend', 'addons', 'all'])) { throw new Exception('Please input correct module name'); } if (!$resource || !in_array($resource, ['js', 'css', 'all'])) { throw new Exception('Please input correct resource name'); } - $moduleArr = $module == 'all' ? ['frontend', 'backend'] : [$module]; + $moduleArr = $module == 'all' ? ['frontend', 'backend', 'addons'] : [$module]; $resourceArr = $resource == 'all' ? ['js', 'css'] : [$resource]; $minPath = __DIR__ . DS . 'Min' . DS; diff --git a/application/admin/controller/Addon.php b/application/admin/controller/Addon.php index a0bc0562cb09eb16ea4945c92998cba0b0d66bd9..b52c42ef1c9b3bc11942f648cfd0c018bc9625e1 100644 --- a/application/admin/controller/Addon.php +++ b/application/admin/controller/Addon.php @@ -90,6 +90,10 @@ class Addon extends Backend } $this->error(__('Parameter %s can not be empty', '')); } + $instance = get_addon_instance($name); + if ($instance->configView) { + $this->redirect('/addons/' . $name . '/index/config'); + } $tips = []; foreach ($config as $index => &$item) { if ($item['name'] == '__tips__') { @@ -98,8 +102,6 @@ class Addon extends Backend } } $this->view->assign("addon", ['info' => $info, 'config' => $config, 'tips' => $tips]); - $configFile = ADDON_PATH . $name . DS . 'config.html'; - $viewFile = is_file($configFile) ? $configFile : ''; return $this->view->fetch($viewFile); } diff --git a/public/assets/js/require-addons.js b/public/assets/js/require-addons.js new file mode 100644 index 0000000000000000000000000000000000000000..15e42b2fde4703b8a2944af16874841d0756b6bc --- /dev/null +++ b/public/assets/js/require-addons.js @@ -0,0 +1,137 @@ +require.config({ + urlArgs: "v=" + requirejs.s.contexts._.config.config.info.version, + packages: [{ + name: 'moment', + location: '../libs/moment', + main: 'moment' + }], + paths: { + 'lang': "empty:", + 'form': 'require-form', + 'table': 'require-table', + 'upload': 'require-upload', + 'drag': 'jquery.drag.min', + 'drop': 'jquery.drop.min', + 'dropzone': 'dropzone.min', + 'echarts': 'echarts.min', + 'echarts-theme': 'echarts-theme', + 'adminlte': 'adminlte', + 'bootstrap-table-commonsearch': 'bootstrap-table-commonsearch', + 'bootstrap-table-template': 'bootstrap-table-template', + // + // 以下的包从bower的libs目录加载 + 'jquery': '../libs/jquery/dist/jquery.min', + 'bootstrap': '../libs/bootstrap/dist/js/bootstrap.min', + 'bootstrap-datetimepicker': '../libs/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min', + 'bootstrap-daterangepicker': '../libs/bootstrap-daterangepicker/daterangepicker', + 'bootstrap-select': '../libs/bootstrap-select/dist/js/bootstrap-select.min', + 'bootstrap-select-lang': '../libs/bootstrap-select/dist/js/i18n/defaults-zh_CN', + 'bootstrap-table': '../libs/bootstrap-table/dist/bootstrap-table.min', + 'bootstrap-table-export': '../libs/bootstrap-table/dist/extensions/export/bootstrap-table-export.min', + 'bootstrap-table-fixed-columns': '../libs/bootstrap-table/dist/extensions/fixed-columns/bootstrap-table-fixed-columns', + 'bootstrap-table-mobile': '../libs/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile', + 'bootstrap-table-lang': '../libs/bootstrap-table/dist/locale/bootstrap-table-zh-CN', + 'bootstrap-table-jumpto': '../libs/bootstrap-table/dist/extensions/page-jumpto/bootstrap-table-jumpto', + 'bootstrap-slider': '../libs/bootstrap-slider/bootstrap-slider', + 'tableexport': '../libs/tableExport.jquery.plugin/tableExport.min', + 'dragsort': '../libs/fastadmin-dragsort/jquery.dragsort', + 'sortable': '../libs/Sortable/Sortable.min', + 'addtabs': '../libs/fastadmin-addtabs/jquery.addtabs', + 'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll', + 'validator': '../libs/nice-validator/dist/jquery.validator', + 'validator-lang': '../libs/nice-validator/dist/local/zh-CN', + 'toastr': '../libs/toastr/toastr', + 'jstree': '../libs/jstree/dist/jstree.min', + 'layer': '../libs/fastadmin-layer/dist/layer', + 'cookie': '../libs/jquery.cookie/jquery.cookie', + 'cxselect': '../libs/fastadmin-cxselect/js/jquery.cxselect', + 'template': '../libs/art-template/dist/template-native', + 'selectpage': '../libs/fastadmin-selectpage/selectpage', + 'citypicker': '../libs/fastadmin-citypicker/dist/js/city-picker.min', + 'citypicker-data': '../libs/fastadmin-citypicker/dist/js/city-picker.data', + }, + // shim依赖配置 + shim: { + 'addons': ['backend'], + 'bootstrap': ['jquery'], + 'bootstrap-table': { + deps: ['bootstrap'], + exports: '$.fn.bootstrapTable' + }, + 'bootstrap-table-lang': { + deps: ['bootstrap-table'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'bootstrap-table-export': { + deps: ['bootstrap-table', 'tableexport'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'bootstrap-table-fixed-columns': { + deps: ['bootstrap-table'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'bootstrap-table-mobile': { + deps: ['bootstrap-table'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'bootstrap-table-advancedsearch': { + deps: ['bootstrap-table'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'bootstrap-table-commonsearch': { + deps: ['bootstrap-table'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'bootstrap-table-template': { + deps: ['bootstrap-table', 'template'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'bootstrap-table-jumpto': { + deps: ['bootstrap-table'], + exports: '$.fn.bootstrapTable.defaults' + }, + 'tableexport': { + deps: ['jquery'], + exports: '$.fn.extend' + }, + 'slimscroll': { + deps: ['jquery'], + exports: '$.fn.extend' + }, + 'adminlte': { + deps: ['bootstrap', 'slimscroll'], + exports: '$.AdminLTE' + }, + 'bootstrap-daterangepicker': [ + 'moment/locale/zh-cn' + ], + 'bootstrap-datetimepicker': [ + 'moment/locale/zh-cn', + ], + 'bootstrap-select-lang': ['bootstrap-select'], + 'jstree': ['css!../libs/jstree/dist/themes/default/style.css'], + 'validator-lang': ['validator'], + 'citypicker': ['citypicker-data', 'css!../libs/fastadmin-citypicker/dist/css/city-picker.css'] + }, + baseUrl: requirejs.s.contexts._.config.config.site.cdnurl + '/assets/js/', //资源基础路径 + waitSeconds: 30, + charset: 'utf-8' // 文件编码 +}); + +require(['jquery', 'bootstrap'], function ($) { + //初始配置 + var Config = requirejs.s.contexts._.config.config; + //将Config渲染到全局 + window.Config = Config; + // 配置语言包的路径 + var paths = {}; + paths['lang'] = '/addons/' + Config.info.name + '/index/lang?callback=define&lang=' + Config.lang; + // 避免目录冲突 + paths['backend/'] = 'backend/'; + require.config({paths: paths}); + + // 初始化 + $(function () { + require(['fast']); + }); +}); diff --git a/public/assets/js/require-addons.min.js b/public/assets/js/require-addons.min.js new file mode 100644 index 0000000000000000000000000000000000000000..7c3c2fbc614fb0ddf4fe675ff669b1b36c487796 --- /dev/null +++ b/public/assets/js/require-addons.min.js @@ -0,0 +1,5 @@ +if(!function(t,e){"object"==typeof module&&"object"==typeof module.exports?module.exports=t.document?e(t,!0):function(t){if(!t.document)throw new Error("jQuery requires a window with a document");return e(t)}:e(t)}("undefined"!=typeof window?window:this,function(t,e){function n(t){var e=!!t&&"length"in t&&t.length,n=rt.type(t);return"function"!==n&&!rt.isWindow(t)&&("array"===n||0===e||"number"==typeof e&&e>0&&e-1 in t)}function i(t,e,n){if(rt.isFunction(e))return rt.grep(t,function(t,i){return!!e.call(t,i,t)!==n});if(e.nodeType)return rt.grep(t,function(t){return t===e!==n});if("string"==typeof e){if(gt.test(e))return rt.filter(e,t,n);e=rt.filter(e,t)}return rt.grep(t,function(t){return Z.call(e,t)>-1!==n})}function o(t,e){for(;(t=t[e])&&1!==t.nodeType;);return t}function r(t){var e={};return rt.each(t.match(wt)||[],function(t,n){e[n]=!0}),e}function s(){Y.removeEventListener("DOMContentLoaded",s),t.removeEventListener("load",s),rt.ready()}function a(){this.expando=rt.expando+a.uid++}function l(t,e,n){var i;if(void 0===n&&1===t.nodeType)if(i="data-"+e.replace(Nt,"-$&").toLowerCase(),n=t.getAttribute(i),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:St.test(n)?rt.parseJSON(n):n)}catch(t){}$t.set(t,e,n)}else n=void 0;return n}function u(t,e,n,i){var o,r=1,s=20,a=i?function(){return i.cur()}:function(){return rt.css(t,e,"")},l=a(),u=n&&n[3]||(rt.cssNumber[e]?"":"px"),c=(rt.cssNumber[e]||"px"!==u&&+l)&&jt.exec(rt.css(t,e));if(c&&c[3]!==u){u=u||c[3],n=n||[],c=+l||1;do r=r||".5",c/=r,rt.style(t,e,c+u);while(r!==(r=a()/l)&&1!==r&&--s)}return n&&(c=+c||+l||0,o=n[1]?c+(n[1]+1)*n[2]:+n[2],i&&(i.unit=u,i.start=c,i.end=o)),o}function c(t,e){var n="undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e||"*"):"undefined"!=typeof t.querySelectorAll?t.querySelectorAll(e||"*"):[];return void 0===e||e&&rt.nodeName(t,e)?rt.merge([t],n):n}function p(t,e){for(var n=0,i=t.length;i>n;n++)Et.set(t[n],"globalEval",!e||Et.get(e[n],"globalEval"))}function d(t,e,n,i,o){for(var r,s,a,l,u,d,f=e.createDocumentFragment(),h=[],g=0,m=t.length;m>g;g++)if(r=t[g],r||0===r)if("object"===rt.type(r))rt.merge(h,r.nodeType?[r]:r);else if(Ht.test(r)){for(s=s||f.appendChild(e.createElement("div")),a=(It.exec(r)||["",""])[1].toLowerCase(),l=Rt[a]||Rt._default,s.innerHTML=l[1]+rt.htmlPrefilter(r)+l[2],d=l[0];d--;)s=s.lastChild;rt.merge(h,s.childNodes),s=f.firstChild,s.textContent=""}else h.push(e.createTextNode(r));for(f.textContent="",g=0;r=h[g++];)if(i&&rt.inArray(r,i)>-1)o&&o.push(r);else if(u=rt.contains(r.ownerDocument,r),s=c(f.appendChild(r),"script"),u&&p(s),n)for(d=0;r=s[d++];)Lt.test(r.type||"")&&n.push(r);return f}function f(){return!0}function h(){return!1}function g(){try{return Y.activeElement}catch(t){}}function m(t,e,n,i,o,r){var s,a;if("object"==typeof e){"string"!=typeof n&&(i=i||n,n=void 0);for(a in e)m(t,a,n,i,e[a],r);return t}if(null==i&&null==o?(o=n,i=n=void 0):null==o&&("string"==typeof n?(o=i,i=void 0):(o=i,i=n,n=void 0)),o===!1)o=h;else if(!o)return t;return 1===r&&(s=o,o=function(t){return rt().off(t),s.apply(this,arguments)},o.guid=s.guid||(s.guid=rt.guid++)),t.each(function(){rt.event.add(this,e,o,i,n)})}function v(t,e){return rt.nodeName(t,"table")&&rt.nodeName(11!==e.nodeType?e:e.firstChild,"tr")?t.getElementsByTagName("tbody")[0]||t.appendChild(t.ownerDocument.createElement("tbody")):t}function y(t){return t.type=(null!==t.getAttribute("type"))+"/"+t.type,t}function b(t){var e=Ut.exec(t.type);return e?t.type=e[1]:t.removeAttribute("type"),t}function x(t,e){var n,i,o,r,s,a,l,u;if(1===e.nodeType){if(Et.hasData(t)&&(r=Et.access(t),s=Et.set(e,r),u=r.events)){delete s.handle,s.events={};for(o in u)for(n=0,i=u[o].length;i>n;n++)rt.event.add(e,o,u[o][n])}$t.hasData(t)&&(a=$t.access(t),l=rt.extend({},a),$t.set(e,l))}}function w(t,e){var n=e.nodeName.toLowerCase();"input"===n&&qt.test(t.type)?e.checked=t.checked:"input"!==n&&"textarea"!==n||(e.defaultValue=t.defaultValue)}function T(t,e,n,i){e=J.apply([],e);var o,r,s,a,l,u,p=0,f=t.length,h=f-1,g=e[0],m=rt.isFunction(g);if(m||f>1&&"string"==typeof g&&!it.checkClone&&_t.test(g))return t.each(function(o){var r=t.eq(o);m&&(e[0]=g.call(this,o,r.html())),T(r,e,n,i)});if(f&&(o=d(e,t[0].ownerDocument,!1,t,i),r=o.firstChild,1===o.childNodes.length&&(o=r),r||i)){for(s=rt.map(c(o,"script"),y),a=s.length;f>p;p++)l=o,p!==h&&(l=rt.clone(l,!0,!0),a&&rt.merge(s,c(l,"script"))),n.call(t[p],l,p);if(a)for(u=s[s.length-1].ownerDocument,rt.map(s,b),p=0;a>p;p++)l=s[p],Lt.test(l.type||"")&&!Et.access(l,"globalEval")&&rt.contains(u,l)&&(l.src?rt._evalUrl&&rt._evalUrl(l.src):rt.globalEval(l.textContent.replace(zt,"")))}return t}function C(t,e,n){for(var i,o=e?rt.filter(e,t):t,r=0;null!=(i=o[r]);r++)n||1!==i.nodeType||rt.cleanData(c(i)),i.parentNode&&(n&&rt.contains(i.ownerDocument,i)&&p(c(i,"script")),i.parentNode.removeChild(i));return t}function k(t,e){var n=rt(e.createElement(t)).appendTo(e.body),i=rt.css(n[0],"display");return n.detach(),i}function E(t){var e=Y,n=Xt[t];return n||(n=k(t,e),"none"!==n&&n||(Vt=(Vt||rt("