Browse Source

[更新]同步Framework代码

Anyon 5 years ago
parent
commit
f95fa00fbb
55 changed files with 2550 additions and 772 deletions
  1. 86 0
      application/admin/controller/Oplog.php
  2. 2 1
      application/admin/controller/api/Update.php
  3. 122 0
      application/admin/queue/JobsQueue.php
  4. 176 0
      application/admin/service/AuthService.php
  5. 72 0
      application/admin/service/MessageService.php
  6. 55 0
      application/admin/service/OplogService.php
  7. 128 0
      application/admin/service/QueueService.php
  8. 65 0
      application/admin/view/oplog/index.html
  9. 56 0
      application/admin/view/oplog/index_search.html
  10. 1 1
      application/index/controller/Index.php
  11. 67 0
      application/service/handler/PublishHandler.php
  12. 64 0
      application/service/handler/ReceiveHandler.php
  13. 160 0
      application/service/handler/WechatHandler.php
  14. 52 0
      application/service/service/BuildService.php
  15. 150 0
      application/service/service/WechatService.php
  16. 29 17
      application/store/command/AutoRun.php
  17. 20 2
      application/store/controller/Config.php
  18. 1 1
      application/store/controller/ExpressCompany.php
  19. 1 1
      application/store/controller/ExpressProvince.php
  20. 0 5
      application/store/controller/ExpressTemplate.php
  21. 4 4
      application/store/controller/Goods.php
  22. 1 1
      application/store/controller/GoodsCate.php
  23. 2 1
      application/store/controller/Member.php
  24. 10 1
      application/store/controller/Message.php
  25. 1 1
      application/store/controller/Order.php
  26. 2 2
      application/store/controller/api/Notify.php
  27. 2 2
      application/store/controller/api/member/Center.php
  28. 3 3
      application/store/controller/api/member/Order.php
  29. 2 2
      application/store/service/ExpressService.php
  30. 2 2
      application/store/service/ExtendService.php
  31. 2 2
      application/store/service/GoodsService.php
  32. 3 3
      application/store/service/OrderService.php
  33. 33 64
      application/store/view/config/index.html
  34. 51 0
      application/store/view/config/sms.html
  35. 56 56
      application/store/view/express_company/index.html
  36. 55 56
      application/store/view/express_province/index.html
  37. 2 2
      application/store/view/express_template/index.html
  38. 107 198
      application/store/view/goods/form.html
  39. 87 0
      application/store/view/goods/form_style.html
  40. 97 101
      application/store/view/goods/index.html
  41. 2 2
      application/store/view/goods/index_search.html
  42. 15 13
      application/store/view/goods_cate/form.html
  43. 60 60
      application/store/view/goods_cate/index.html
  44. 1 1
      application/store/view/goods_cate/index_search.html
  45. 41 41
      application/store/view/member/index.html
  46. 36 30
      application/store/view/message/index.html
  47. 80 83
      application/store/view/order/index.html
  48. 0 1
      application/store/view/order/index_search.html
  49. 75 0
      application/wechat/queue/WechatQueue.php
  50. 68 0
      application/wechat/service/FansService.php
  51. 83 0
      application/wechat/service/MediaService.php
  52. 248 0
      application/wechat/service/WechatService.php
  53. 1 1
      vendor/autoload.php
  54. 7 7
      vendor/composer/autoload_real.php
  55. 4 4
      vendor/composer/autoload_static.php

+ 86 - 0
application/admin/controller/Oplog.php

@@ -0,0 +1,86 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\admin\controller;
+
+use library\Controller;
+use think\Db;
+
+/**
+ * 系统日志管理
+ * Class Oplog
+ * @package app\admin\controller
+ */
+class Oplog extends Controller
+{
+
+    /**
+     * 指定当前数据表
+     * @var string
+     */
+    public $table = 'SystemLog';
+
+    /**
+     * 系统操作日志
+     * @throws \think\Exception
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public function index()
+    {
+        $this->title = '系统操作日志';
+        $query = $this->_query($this->table)->like('action,node,content,username,geoip');
+        $query->dateBetween('create_at')->order('id desc')->page();
+    }
+
+    /**
+     * 列表数据处理
+     * @param array $data
+     * @throws \Exception
+     */
+    protected function _index_page_filter(&$data)
+    {
+        $ip = new \Ip2Region();
+        foreach ($data as &$vo) {
+            $result = $ip->btreeSearch($vo['geoip']);
+            $vo['isp'] = isset($result['region']) ? $result['region'] : '';
+            $vo['isp'] = str_replace(['内网IP', '0', '|'], '', $vo['isp']);
+        }
+    }
+
+    /**
+     * 清理系统日志
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public function clear()
+    {
+        if (Db::name($this->table)->whereRaw('1=1')->delete() !== false) {
+            $this->success('日志清理成功!');
+        } else {
+            $this->error('日志清理失败,请稍候再试!');
+        }
+    }
+
+    /**
+     * 删除系统日志
+     */
+    public function remove()
+    {
+        $this->applyCsrfToken();
+        $this->_delete($this->table);
+    }
+
+}

+ 2 - 1
application/admin/controller/api/Update.php

@@ -47,7 +47,8 @@ class Update extends Controller
         $file = env('root_path') . decode($encode);
         if (!file_exists($file)) $this->error('获取文件内容失败!');
         $this->success('获取文件内容成功!', [
-            'format' => 'base64', 'content' => base64_encode(file_get_contents($file)),
+            'format'  => 'base64',
+            'content' => base64_encode(file_get_contents($file)),
         ]);
     }
 

+ 122 - 0
application/admin/queue/JobsQueue.php

@@ -0,0 +1,122 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\admin\queue;
+
+use app\admin\service\QueueService;
+
+/**
+ * 基础指令公共类
+ * Class QueueBase
+ * @package app\admin
+ */
+class JobsQueue
+{
+    /**
+     * 待处理
+     */
+    const STATUS_PEND = 1;
+
+    /**
+     * 处理中
+     */
+    const STATUS_PROC = 2;
+
+    /**
+     * 处理完成
+     */
+    const STATUS_COMP = 3;
+
+    /**
+     * 处理失败
+     */
+    const STATUS_FAIL = 4;
+
+    /**
+     * 任务ID
+     * @var integer
+     */
+    protected $id;
+
+    /**
+     * 任务数据
+     * @var array
+     */
+    protected $data;
+
+    /**
+     * 任务名称
+     * @var string
+     */
+    protected $title;
+
+    /**
+     * 任务状态
+     * @var integer
+     */
+    protected $status;
+
+    /**
+     * @var \think\console\Output
+     */
+    protected $output;
+
+    /**
+     * 任务状态描述
+     * @var string
+     */
+    protected $statusDesc = '';
+
+    /**
+     * 启动任务处理
+     * @param \think\queue\Job $job 当前任务对象
+     * @param array $data 任务执行对象
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public function fire(\think\queue\Job $job, $data = [])
+    {
+        $this->data = $data;
+        $this->output = new \think\console\Output();
+        $this->id = isset($data['_job_id_']) ? $data['_job_id_'] : '';
+        $this->title = isset($data['_job_title_']) ? $data['_job_title_'] : '';
+        $this->output->newLine();
+        $this->output->writeln("       system task {$this->id} execution start");
+        $this->output->writeln('---------------------------------------------');
+        QueueService::status($this->id, self::STATUS_PROC, $this->statusDesc);
+        if ($this->execute()) {
+            $this->output->writeln('---------------------------------------------');
+            $this->output->info("                successful");
+            $this->status = self::STATUS_COMP;
+        } else {
+            $this->output->writeln('---------------------------------------------');
+            $this->output->error("                failure");
+            $this->status = self::STATUS_FAIL;
+        }
+        $job->delete();
+        QueueService::status($this->id, $this->status, $this->statusDesc);
+        $this->output->writeln('---------------------------------------------');
+        $this->output->newLine();
+    }
+
+    /**
+     * 执行任务
+     * @return boolean
+     */
+    protected function execute()
+    {
+        return true;
+    }
+
+}

+ 176 - 0
application/admin/service/AuthService.php

@@ -0,0 +1,176 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\admin\service;
+
+use library\tools\Data;
+use library\tools\Node;
+use think\Db;
+
+/**
+ * 权限访问及菜单管理
+ * Class AuthService
+ * @package app\admin\service
+ */
+class AuthService
+{
+
+    /**
+     * 权限节点忽略规则
+     * @return array
+     */
+    public static function getIgnore()
+    {
+        return ['index', 'admin/login', 'admin/index'];
+    }
+
+    /**
+     * 获取系统代码节点
+     * @param array $nodes
+     * @return array
+     * @throws \ReflectionException
+     */
+    public static function get($nodes = [])
+    {
+        list($ignore, $map) = [self::getIgnore(), Node::getClassTreeNode(env('app_path'))];
+        $alias = Db::name('SystemNode')->column('node,is_menu,is_auth,is_login,title');
+        foreach (Node::getMethodTreeNode(env('app_path')) as $thr => $title) {
+            foreach ($ignore as $str) if (stripos($thr, $str) === 0) continue 2;
+            $tmp = explode('/', $thr);
+            list($one, $two) = ["{$tmp[0]}", "{$tmp[0]}/{$tmp[1]}"];
+            $nodes[$one] = array_merge(isset($alias[$one]) ? $alias[$one] : ['node' => $one, 'title' => '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => '']);
+            $nodes[$two] = array_merge(isset($alias[$two]) ? $alias[$two] : ['node' => $two, 'title' => isset($map[$two]) ? $map[$two] : '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => $one]);
+            $nodes[$thr] = array_merge(isset($alias[$thr]) ? $alias[$thr] : ['node' => $thr, 'title' => $title, 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => $two]);
+        }
+        foreach ($nodes as &$node) list($node['is_auth'], $node['is_menu'], $node['is_login']) = [intval($node['is_auth']), intval($node['is_menu']), empty($node['is_auth']) ? intval($node['is_login']) : 1];
+        return $nodes;
+    }
+
+    /**
+     * 检查用户节点权限
+     * @param string $node 节点
+     * @return boolean
+     */
+    public static function checkAuthNode($node)
+    {
+        list($module, $controller, $action) = explode('/', str_replace(['?', '=', '&'], '/', "{$node}///"));
+        $current = Node::parseString("{$module}/{$controller}") . strtolower("/{$action}");
+        // 后台入口无需要验证权限
+        if (stripos($node, 'admin/index') === 0) return true;
+        // 超级管理员无需要验证权限
+        if (session('user.username') === 'admin') return true;
+        // 未配置权限的节点默认放行
+        if (!in_array($current, self::getAuthNode())) return true;
+        // 用户指定角色授权放行
+        return in_array($current, (array)session('user.nodes'));
+    }
+
+    /**
+     * 获取授权节点
+     * @return array
+     */
+    public static function getAuthNode()
+    {
+        $nodes = cache('need_access_node');
+        if (empty($nodes)) {
+            $nodes = Db::name('SystemNode')->where(['is_auth' => '1'])->column('node');
+            cache('need_access_node', $nodes);
+        }
+        return $nodes;
+    }
+
+    /**
+     * 应用用户权限节点
+     * @return boolean
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function applyNode()
+    {
+        cache('need_access_node', null);
+        if (($uid = session('user.id'))) session('user', Db::name('SystemUser')->where('id', $uid)->find());
+        if (session('user.authorize') && ($ids = explode(',', session('user.authorize')))) {
+            $auths = Db::name('SystemAuth')->whereIn('id', $ids)->where('status', '1')->column('id');
+            if (empty($auths)) return session('user.nodes', []);
+            return session('user.nodes', Db::name('SystemAuthNode')->whereIn('auth', $auths)->column('node'));
+        }
+        return false;
+    }
+
+    /**
+     * 判断用户登录状态
+     * @return boolean
+     */
+    public static function isLogin()
+    {
+        return !!session('user');
+    }
+
+    /**
+     * 获取授权后的菜单
+     * @return array
+     * @throws \ReflectionException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function getAuthMenu()
+    {
+        self::applyNode();
+        $list = Db::name('SystemMenu')->where('status', '1')->order('sort asc,id asc')->select();
+        return self::buildMenuData(Data::arr2tree($list), self::get(), self::isLogin());
+    }
+
+    /**
+     * 后台主菜单权限过滤
+     * @param array $menus 当前菜单列表
+     * @param array $nodes 系统权限节点数据
+     * @param bool $isLogin 是否已经登录
+     * @return array
+     */
+    private static function buildMenuData($menus, $nodes, $isLogin)
+    {
+        foreach ($menus as $key => &$menu) {
+            if (!empty($menu['sub'])) $menu['sub'] = self::buildMenuData($menu['sub'], $nodes, $isLogin);
+            if (!empty($menu['sub'])) $menu['url'] = '#';
+            elseif (preg_match('/^https?\:/i', $menu['url'])) continue;
+            elseif ($menu['url'] !== '#') {
+                $node = join('/', array_slice(explode('/', preg_replace('/[\W]/', '/', $menu['url'])), 0, 3));
+                $menu['url'] = url($menu['url']) . (empty($menu['params']) ? '' : "?{$menu['params']}");
+                if (isset($nodes[$node]) && $nodes[$node]['is_login'] && empty($isLogin)) unset($menus[$key]);
+                elseif (isset($nodes[$node]) && $nodes[$node]['is_auth'] && $isLogin && !self::checkAuthNode($node)) unset($menus[$key]);
+            } else unset($menus[$key]);
+        }
+        return $menus;
+    }
+
+    /**
+     * 检查密码是否合法
+     * @param string $password
+     * @return array
+     */
+    public static function checkPassword($password)
+    {
+        $password = trim($password);
+        if (!strlen($password) >= 6) {
+            return ['code' => 0, 'msg' => '密码必须大于6字符!'];
+        }
+        if (!preg_match("/^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,32}$/", $password)) {
+            return ['code' => 0, 'msg' => '密码必需包含大小写字母、数字、符号任意两者组合!'];
+        }
+        return ['code' => 1, 'msg' => '密码复杂度通过验证!'];
+    }
+
+}

+ 72 - 0
application/admin/service/MessageService.php

@@ -0,0 +1,72 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\admin\service;
+
+use library\tools\Data;
+use think\Db;
+
+/**
+ * 系统消息管理
+ * Class MessageService
+ * @package app\admin\service
+ */
+class MessageService
+{
+    /**
+     * 增加系统消息
+     * @param string $title 消息标题
+     * @param string $desc 消息描述
+     * @param string $url 访问链接
+     * @param string $node 权限节点
+     * @return boolean
+     */
+    public static function add($title, $desc, $url, $node)
+    {
+        $code = Data::uniqidNumberCode(12);
+        $data = ['title' => $title, 'desc' => $desc, 'url' => $url, 'code' => $code, 'node' => $node];
+        return Db::name('SystemMessage')->insert($data) !== false;
+    }
+
+    /**
+     * 系统消息状态更新
+     * @param integer $code
+     * @return boolean
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function set($code)
+    {
+        $result = Db::name('SystemMessage')->where(['code' => $code, 'read_state' => '0'])->update([
+            'read_state' => '1', 'read_at' => date('Y-m-d H:i:s'), 'read_uid' => session('user.id'),
+        ]);
+        return $result !== false;
+    }
+
+    /**
+     * 获取消息列表成功
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function gets()
+    {
+        $where = ['read_state' => '0'];
+        $list = Db::name('SystemMessage')->where($where)->order('id desc')->select();
+        foreach ($list as $key => $vo) if (!empty($vo['node']) && !auth($vo['node'])) unset($list[$key]);
+        return $list;
+    }
+
+}

+ 55 - 0
application/admin/service/OplogService.php

@@ -0,0 +1,55 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\admin\service;
+
+use library\tools\Node;
+use think\Db;
+
+/**
+ * 系统日志服务管理
+ * Class LogService
+ * @package app\admin\service
+ */
+class OplogService
+{
+    /**
+     * 写入操作日志
+     * @param string $action
+     * @param string $content
+     * @return bool
+     */
+    public static function write($action = '行为', $content = "内容描述")
+    {
+        $data = [
+            'node'     => Node::current(),
+            'geoip'    => PHP_SAPI === 'cli' ? '127.0.0.1' : request()->ip(),
+            'action'   => $action,
+            'content'  => $content,
+            'username' => PHP_SAPI === 'cli' ? 'cli' : (string)session('user.username'),
+        ];
+        return Db::name('SystemLog')->insert($data) !== false;
+    }
+
+    /**
+     * 清理系统日志数据
+     * @return boolean
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function clear()
+    {
+        return Db::name('SystemLog')->where('1=1')->delete() !== false;
+    }
+}

+ 128 - 0
application/admin/service/QueueService.php

@@ -0,0 +1,128 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\admin\service;
+
+use think\Db;
+
+/**
+ * 任务管理器
+ * Class QueueService
+ * @package app\admin\service
+ */
+class QueueService
+{
+    /**
+     * 待处理
+     */
+    const STATUS_PEND = 1;
+
+    /**
+     * 处理中
+     */
+    const STATUS_PROC = 2;
+
+    /**
+     * 处理完成
+     */
+    const STATUS_COMP = 3;
+
+    /**
+     * 处理失败
+     */
+    const STATUS_FAIL = 4;
+
+    /**
+     * 创建任务并记录日志
+     * @param string $title 任务名称
+     * @param string $uri 任务命令
+     * @param integer $later 延时时间
+     * @param array $data 任务附加数据
+     * @param integer $double 任务多开
+     * @param string $desc 任务描述
+     * @throws \think\Exception
+     */
+    public static function add($title, $uri, $later, array $data, $double = 1, $desc = '')
+    {
+        if (empty($double) && self::exists($title)) {
+            throw new \think\Exception('该任务已经创建,请耐心等待处理完成!');
+        }
+        $jobId = Db::name('SystemJobsLog')->insertGetId([
+            'title' => $title, 'later' => $later, 'uri' => $uri, 'double' => intval($double),
+            'data'  => json_encode($data, 256), 'desc' => $desc, 'status_at' => date('Y-m-d H:i:s'),
+        ]);
+        $data['_job_id_'] = $jobId;
+        $data['_job_title_'] = $title;
+        \think\Queue::later($later, $uri, $data);
+    }
+
+    /**
+     * 更新任务状态
+     * @param integer $jobId
+     * @param integer $status
+     * @param string $statusDesc
+     * @return boolean
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function status($jobId, $status = self::STATUS_PEND, $statusDesc = '')
+    {
+        $result = Db::name('SystemJobsLog')->where(['id' => $jobId])->update([
+            'status' => $status, 'status_desc' => $statusDesc, 'status_at' => date('Y-m-d H:i:s'),
+        ]);
+        return $result !== false;
+    }
+
+    /**
+     * 检查任务是否存在
+     * @param string $title
+     * @return boolean
+     */
+    public static function exists($title)
+    {
+        $where = [['title', 'eq', $title], ['status', 'in', [1, 2]]];
+        return Db::name('SystemJobsLog')->where($where)->count() > 0;
+    }
+
+    /**
+     * 获取任务数据
+     * @param integer $jobId
+     * @return array|null
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function get($jobId)
+    {
+        return Db::name('SystemJobsLog')->where(['id' => $jobId])->find();
+    }
+
+    /**
+     * 删除任务数据
+     * @param integer $jobId
+     * @return boolean
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function del($jobId)
+    {
+        $where = [['id', 'eq', $jobId], ['status', 'in', [1, 3, 4]]];
+        if (Db::name('SystemJobsLog')->where($where)->delete() > 0) {
+            Db::name('SystemJobs')->whereLike('payload', '%"_job_id_":"' . $jobId . '"%')->delete();
+            return true;
+        }
+        return false;
+    }
+
+}

+ 65 - 0
application/admin/view/oplog/index.html

@@ -0,0 +1,65 @@
+{extend name='main'}
+
+{block name="button"}
+
+{if auth("admin/oplog/clear")}
+<button data-load='{:url("clear")}' data-confirm="确定要消除所有日志吗?" class='layui-btn layui-btn-sm layui-btn-primary'>清理日志</button>
+{/if}
+
+{if auth("admin/oplog/remove")}
+<button data-action='{:url("remove")}' data-rule="id#{key}" data-csrf="{:systoken('admin/oplog/remove')}" data-confirm="确定要删除日志吗?" class='layui-btn layui-btn-sm layui-btn-primary'>删除日志</button>
+{/if}
+
+{/block}
+
+{block name="content"}
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='oplog/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class='text-left nowrap'>操作账号</th>
+            <th class='text-left nowrap'>权限节点</th>
+            <th class='text-left nowrap'>操作行为</th>
+            <th class='text-left nowrap'>地理位置</th>
+            <th class='text-left nowrap'>操作时间</th>
+            <th></th>
+        </tr>
+        </thead>
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr data-dbclick>
+            <td class='list-table-check-td think-checkbox'>
+                <input class="list-check-box" value='{$vo.id}' type='checkbox'>
+            </td>
+            <td class="text-left nowrap">{$vo.username|default='-'}</td>
+            <td class="text-left nowrap">{$vo.node|default='-'}</td>
+            <td class='text-left nowrap'>
+                <p class="color-text">{$vo.action|default='-'}</p>
+                <p class="color-desc">{$vo.content|default='-'}</p>
+            </td>
+            <td class='text-left nowrap'>
+                <p class="color-text">{$vo.geoip|default='-'}</p>
+                <p class="color-desc">{$vo.isp|default='-'}</p>
+            </td>
+            <td class='text-left nowrap'>{$vo.create_at|format_datetime}</td>
+            <td class='text-left nowrap'>
+                {if auth("admin/oplog/remove")}
+                <a data-dbclick class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除该日志吗?" data-action="{:url('remove')}" data-value="id#{$vo.id}" data-csrf="{:systoken('admin/oplog/remove')}">删 除</a>
+                {/if}
+            </td>
+        </tr>
+        {/foreach}
+        </tbody>
+        {/notempty}
+    </table>
+
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+
+</div>
+
+{/block}

+ 56 - 0
application/admin/view/oplog/index_search.html

@@ -0,0 +1,56 @@
+<fieldset>
+
+    <legend class="layui-bg-cyan">条件搜索</legend>
+
+    <form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
+
+        <div class="layui-form-item layui-inline">
+            <label class="layui-form-label">操作账号</label>
+            <div class="layui-input-inline">
+                <input name="username" value="{$Think.get.username|default=''}" placeholder="请输入操作账号" class="layui-input">
+            </div>
+        </div>
+
+        <div class="layui-form-item layui-inline">
+            <label class="layui-form-label">操作节点</label>
+            <div class="layui-input-inline">
+                <input name="node" value="{$Think.get.node|default=''}" placeholder="请输入操作节点" class="layui-input">
+            </div>
+        </div>
+
+        <div class="layui-form-item layui-inline">
+            <label class="layui-form-label">操作行为</label>
+            <div class="layui-input-inline">
+                <input name="action" value="{$Think.get.action|default=''}" placeholder="请输入操作行为" class="layui-input">
+            </div>
+        </div>
+
+        <div class="layui-form-item layui-inline">
+            <label class="layui-form-label">操作描述</label>
+            <div class="layui-input-inline">
+                <input name="content" value="{$Think.get.content|default=''}" placeholder="请输入操作内容" class="layui-input">
+            </div>
+        </div>
+
+        <div class="layui-form-item layui-inline">
+            <label class="layui-form-label">位置地址</label>
+            <div class="layui-input-inline">
+                <input name="geoip" value="{$Think.get.geoip|default=''}" placeholder="请输入位置地址" class="layui-input">
+            </div>
+        </div>
+
+        <div class="layui-form-item layui-inline">
+            <label class="layui-form-label">操作时间</label>
+            <div class="layui-input-inline">
+                <input data-date-range name="create_at" value="{$Think.get.create_at|default=''}" placeholder="请选择操作时间" class="layui-input">
+            </div>
+        </div>
+
+        <div class="layui-form-item layui-inline">
+            <button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
+        </div>
+    </form>
+
+</fieldset>
+
+<script>form.render()</script>

+ 1 - 1
application/index/controller/Index.php

@@ -9,7 +9,7 @@
 // +----------------------------------------------------------------------
 // | 开源协议 ( https://mit-license.org )
 // +----------------------------------------------------------------------
-// | github开源项目:https://github.com/zoujingli/ThinkAdmin
+// | github开源项目:https://github.com/zoujingli/framework
 // +----------------------------------------------------------------------
 
 namespace app\index\controller;

+ 67 - 0
application/service/handler/PublishHandler.php

@@ -0,0 +1,67 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\service\handler;
+
+use app\service\service\WechatService;
+
+/**
+ * 第三方平台测试上线
+ *
+ * @author Anyon <zoujingli@qq.com>
+ * @date 2016/10/27 14:14
+ */
+class PublishHandler
+{
+
+    /**
+     * 当前微信APPID
+     * @var string
+     */
+    protected static $appid;
+
+    /**
+     * 事件初始化
+     * @param string $appid
+     * @return string
+     * @throws \WeChat\Exceptions\InvalidDecryptException
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function handler($appid)
+    {
+        try {
+            $wechat = WechatService::WeChatReceive($appid);
+        } catch (\Exception $e) {
+            return "Wechat message handling failed, {$e->getMessage()}";
+        }
+        /* 分别执行对应类型的操作 */
+        switch (strtolower($wechat->getMsgType())) {
+            case 'text':
+                $receive = $wechat->getReceive();
+                if ($receive['Content'] === 'TESTCOMPONENT_MSG_TYPE_TEXT') {
+                    return $wechat->text('TESTCOMPONENT_MSG_TYPE_TEXT_callback')->reply([], true);
+                }
+                $key = str_replace("QUERY_AUTH_CODE:", '', $receive['Content']);
+                WechatService::instance('Service')->getQueryAuthorizerInfo($key);
+                return $wechat->text("{$key}_from_api")->reply([], true);
+            case 'event':
+                $receive = $wechat->getReceive();
+                return $wechat->text("{$receive['Event']}from_callback")->reply([], true);
+            default:
+                return 'success';
+        }
+    }
+
+}

+ 64 - 0
application/service/handler/ReceiveHandler.php

@@ -0,0 +1,64 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\service\handler;
+
+use app\service\service\WechatService;
+use think\Db;
+
+/**
+ * 微信推送消息处理
+ *
+ * @author Anyon <zoujingli@qq.com>
+ * @date 2016/10/27 14:14
+ */
+class ReceiveHandler
+{
+
+    /**
+     * 事件初始化
+     * @param string $appid
+     * @return string
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     * @throws \think\Exception
+     */
+    public static function handler($appid)
+    {
+        try {
+            $service = WechatService::WeChatReceive($appid);
+        } catch (\Exception $e) {
+            return "Wechat message handling failed, {$e->getMessage()}";
+        }
+        // 验证微信配置信息
+        $config = Db::name('WechatServiceConfig')->where(['authorizer_appid' => $appid])->find();
+        if (empty($config) || empty($config['appuri'])) {
+            \think\facade\Log::error(($message = "微信{$appid}授权配置验证无效"));
+            return $message;
+        }
+        try {
+            list($data, $openid) = [$service->getReceive(), $service->getOpenid()];
+            if (isset($data['EventKey']) && is_object($data['EventKey'])) $data['EventKey'] = (array)$data['EventKey'];
+            $input = ['openid' => $openid, 'appid' => $appid, 'receive' => serialize($data), 'encrypt' => intval($service->isEncrypt())];
+            if (is_string($result = http_post($config['appuri'], $input, ['timeout' => 30]))) {
+                return $result;
+            }
+        } catch (\Exception $e) {
+            \think\facade\Log::error("微信{$appid}接口调用异常,{$e->getMessage()}");
+        }
+        return 'success';
+    }
+
+}

+ 160 - 0
application/service/handler/WechatHandler.php

@@ -0,0 +1,160 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\service\handler;
+
+use app\service\service\WechatService as WechatLogic;
+use think\Db;
+
+/**
+ * 微信网页授权接口
+ * Class WechatHandler
+ * @package app\wechat\handler
+ * @author Anyon <zoujingli@qq.com>
+ */
+class WechatHandler
+{
+    /**
+     * 当前微信APPID
+     * @var string
+     */
+    protected $appid;
+
+    /**
+     * 当前微信配置
+     * @var array
+     */
+    protected $config;
+
+    /**
+     * 错误消息
+     * @var string
+     */
+    protected $message;
+
+    /**
+     * Wechat constructor.
+     * @param array $config
+     */
+    public function __construct($config = [])
+    {
+        $this->config = $config;
+        $this->appid = isset($config['authorizer_appid']) ? $config['authorizer_appid'] : '';
+    }
+
+    /**
+     * 检查微信配置服务初始化状态
+     * @return boolean
+     * @throws \think\Exception
+     */
+    private function checkInit()
+    {
+        if (!empty($this->config)) return true;
+        throw new \think\Exception('Wechat Please bind Wechat first');
+    }
+
+    /**
+     * 获取当前公众号配置
+     * @return array|boolean
+     * @throws \think\Exception
+     */
+    public function getConfig()
+    {
+        $this->checkInit();
+        $info = Db::name('WechatServiceConfig')->where(['authorizer_appid' => $this->appid])->find();
+        if (empty($info)) return false;
+        if (isset($info['id'])) unset($info['id']);
+        return $info;
+    }
+
+    /**
+     * 设置微信接口通知URL地址
+     * @param string $notifyUri 接口通知URL地址
+     * @return boolean
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public function setApiNotifyUri($notifyUri)
+    {
+        $this->checkInit();
+        if (empty($notifyUri)) throw new \think\Exception('请传入微信通知URL');
+        list($where, $data) = [['authorizer_appid' => $this->appid], ['appuri' => $notifyUri]];
+        return Db::name('WechatServiceConfig')->where($where)->update($data) !== false;
+    }
+
+    /**
+     * 更新接口Appkey(成功返回新的Appkey)
+     * @return bool|string
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public function updateApiAppkey()
+    {
+        $this->checkInit();
+        $data = ['appkey' => md5(uniqid())];
+        Db::name('WechatServiceConfig')->where(['authorizer_appid' => $this->appid])->update($data);
+        return $data['appkey'];
+    }
+
+    /**
+     * 获取公众号的配置参数
+     * @param string $name 参数名称
+     * @return array|string
+     * @throws \think\Exception
+     */
+    public function config($name = null)
+    {
+        $this->checkInit();
+        return WechatLogic::WeChatScript($this->appid)->config->get($name);
+    }
+
+    /**
+     * 微信网页授权
+     * @param string $sessid 当前会话id(可用session_id()获取)
+     * @param string $selfUrl 当前会话URL地址(需包含域名的完整URL地址)
+     * @param int $fullMode 网页授权模式(0静默模式,1高级授权)
+     * @return array|bool
+     * @throws \think\Exception
+     */
+    public function oauth($sessid, $selfUrl, $fullMode = 0)
+    {
+        $this->checkInit();
+        $fans = cache("{$this->appid}_{$sessid}_fans");
+        $openid = cache("{$this->appid}_{$sessid}_openid");
+        if (!empty($openid) && (empty($fullMode) || !empty($fans))) {
+            return ['openid' => $openid, 'fans' => $fans, 'url' => ''];
+        }
+        $service = WechatLogic::service();
+        $mode = empty($fullMode) ? 'snsapi_base' : 'snsapi_userinfo';
+        $url = url('@service/api.push/oauth', '', true, true);
+        $params = ['mode' => $fullMode, 'sessid' => $sessid, 'enurl' => encode($selfUrl)];
+        $authurl = $service->getOauthRedirect($this->appid, $url . '?' . http_build_query($params), $mode);
+        return ['openid' => $openid, 'fans' => $fans, 'url' => $authurl];
+    }
+
+    /**
+     * 微信网页JS签名
+     * @param string $url 当前会话URL地址(需包含域名的完整URL地址)
+     * @return array|boolean
+     * @throws \WeChat\Exceptions\InvalidResponseException
+     * @throws \WeChat\Exceptions\LocalCacheException
+     * @throws \think\Exception
+     */
+    public function jsSign($url)
+    {
+        $this->checkInit();
+        return WechatLogic::WeChatScript($this->appid)->getJsSign($url);
+    }
+
+}

+ 52 - 0
application/service/service/BuildService.php

@@ -0,0 +1,52 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\service\service;
+
+/**
+ * 授权数据处理
+ * Class Build
+ * @package app\service\service
+ */
+class BuildService
+{
+
+    /**
+     * 授权数据过滤转换处理
+     * @param array $info
+     * @return mixed
+     */
+    public static function filter(array $info)
+    {
+        if (isset($info['func_info'])) $info['func_info'] = join(',', array_map(function ($tmp) {
+            return $tmp['funcscope_category']['id'];
+        }, $info['func_info']));
+        $info['verify_type_info'] = join(',', $info['verify_type_info']);
+        $info['service_type_info'] = join(',', $info['service_type_info']);
+        $info['business_info'] = json_encode($info['business_info'], JSON_UNESCAPED_UNICODE);
+        // 微信类型:  0 代表订阅号, 2 代表服务号, 3 代表小程序
+        $info['service_type'] = intval($info['service_type_info']) === 2 ? 2 : 0;
+        if (!empty($info['MiniProgramInfo'])) {
+            // 微信类型:  0 代表订阅号, 2 代表服务号, 3 代表小程序
+            $info['service_type'] = 3;
+            // 小程序信息
+            $info['miniprograminfo'] = json_encode($info['MiniProgramInfo'], JSON_UNESCAPED_UNICODE);
+        }
+        unset($info['MiniProgramInfo']);
+        // 微信认证: -1 代表未认证, 0 代表微信认证
+        $info['verify_type'] = intval($info['verify_type_info']) !== 0 ? -1 : 0;
+        return $info;
+    }
+
+}

+ 150 - 0
application/service/service/WechatService.php

@@ -0,0 +1,150 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\service\service;
+
+use think\Db;
+
+/**
+ * 微信数据服务
+ * Class WechatService
+ * @package app\service\service
+ * @method \WeChat\Card WeChatCard($appid) static 微信卡券管理
+ * @method \WeChat\Custom WeChatCustom($appid) static 微信客服消息
+ * @method \WeChat\Limit WeChatLimit($appid) static 接口调用频次限制
+ * @method \WeChat\Media WeChatMedia($appid) static 微信素材管理
+ * @method \WeChat\Menu WeChatMenu($appid) static 微信菜单管理
+ * @method \WeChat\Oauth WeChatOauth($appid) static 微信网页授权
+ * @method \WeChat\Pay WeChatPay($appid) static 微信支付商户
+ * @method \WeChat\Product WeChatProduct($appid) static 微信商店管理
+ * @method \WeChat\Qrcode WeChatQrcode($appid) static 微信二维码管理
+ * @method \WeChat\Receive WeChatReceive($appid) static 微信推送管理
+ * @method \WeChat\Scan WeChatScan($appid) static 微信扫一扫接入管理
+ * @method \WeChat\Script WeChatScript($appid) static 微信前端支持
+ * @method \WeChat\Shake WeChatShake($appid) static 微信揺一揺周边
+ * @method \WeChat\Tags WeChatTags($appid) static 微信用户标签管理
+ * @method \WeChat\Template WeChatTemplate($appid) static 微信模板消息
+ * @method \WeChat\User WeChatUser($appid) static 微信粉丝管理
+ * @method \WeChat\Wifi WeChatWifi($appid) static 微信门店WIFI管理
+ *
+ * ----- WeMini -----
+ * @method \WeMini\Account WeMiniAccount($appid) static 小程序账号管理
+ * @method \WeMini\Basic WeMiniBasic($appid) static 小程序基础信息设置
+ * @method \WeMini\Code WeMiniCode($appid) static 小程序代码管理
+ * @method \WeMini\Domain WeMiniDomain($appid) static 小程序域名管理
+ * @method \WeMini\Tester WeMinitester($appid) static 小程序成员管理
+ * @method \WeMini\User WeMiniUser($appid) static 小程序帐号管理
+ * --------------------
+ * @method \WeMini\Crypt WeMiniCrypt($appid) static 小程序数据加密处理
+ * @method \WeMini\Plugs WeMiniPlugs($appid) static 小程序插件管理
+ * @method \WeMini\Poi WeMiniPoi($appid) static 小程序地址管理
+ * @method \WeMini\Qrcode WeMiniQrcode($appid) static 小程序二维码管理
+ * @method \WeMini\Template WeMiniTemplate($appid) static 小程序模板消息支持
+ * @method \WeMini\Total WeMiniTotal($appid) static 小程序数据接口
+ *
+ * ----- WePay -----
+ * @method \WePay\Bill WePayBill($appid) static 微信商户账单及评论
+ * @method \WePay\Order WePayOrder($appid) static 微信商户订单
+ * @method \WePay\Refund WePayRefund($appid) static 微信商户退款
+ * @method \WePay\Coupon WePayCoupon($appid) static 微信商户代金券
+ * @method \WePay\Redpack WePayRedpack($appid) static 微信红包支持
+ * @method \WePay\Transfers WePayTransfers($appid) static 微信商户打款到零钱
+ * @method \WePay\TransfersBank WePayTransfersBank($appid) static 微信商户打款到银行卡
+ *
+ * ----- WeOpen -----
+ * @method \WeOpen\Login login() static 第三方微信登录
+ * @method \WeOpen\Service service() static 第三方服务
+ *
+ * ----- ThinkService -----
+ * @method mixed wechat() static 第三方微信工具
+ * @method mixed config() static 第三方配置工具
+ */
+class WechatService
+{
+
+    /**
+     * 接口类型模式
+     * @var string
+     */
+    private static $type = 'WeChat';
+
+    /**
+     * 实例微信对象
+     * @param string $name
+     * @param string $appid 授权公众号APPID
+     * @param string $type 将要获取SDK类型
+     * @return mixed
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function instance($name, $appid = '', $type = null)
+    {
+        $config = [
+            'cache_path'               => env('runtime_path') . 'wechat',
+            'component_appid'          => sysconf('component_appid'),
+            'component_token'          => sysconf('component_token'),
+            'component_appsecret'      => sysconf('component_appsecret'),
+            'component_encodingaeskey' => sysconf('component_encodingaeskey'),
+        ];
+        // 注册授权公众号 AccessToken 处理
+        $config['GetAccessTokenCallback'] = function ($authorizerAppid) use ($config) {
+            $where = ['authorizer_appid' => $authorizerAppid];
+            if (!($refreshToken = Db::name('WechatServiceConfig')->where($where)->value('authorizer_refresh_token'))) {
+                throw new \think\Exception('The WeChat information is not configured.', '404');
+            }
+            $open = new \WeOpen\MiniApp($config);
+            $result = $open->refreshAccessToken($authorizerAppid, $refreshToken);
+            if (empty($result['authorizer_access_token']) || empty($result['authorizer_refresh_token'])) {
+                throw new \think\Exception($result['errmsg'], '0');
+            }
+            Db::name('WechatServiceConfig')->where($where)->update([
+                'authorizer_access_token'  => $result['authorizer_access_token'],
+                'authorizer_refresh_token' => $result['authorizer_refresh_token'],
+            ]);
+            return $result['authorizer_access_token'];
+        };
+        $app = new \WeOpen\MiniApp($config);
+        if (in_array(strtolower($name), ['service', 'miniapp'])) {
+            return $app;
+        }
+        if (!in_array($type, ['WeChat', 'WeMini'])) {
+            $type = self::$type;
+        }
+        return $app->instance($name, $appid, $type);
+    }
+
+    /**
+     * 静态初始化对象
+     * @param string $name
+     * @param array $arguments
+     * @return mixed
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function __callStatic($name, $arguments)
+    {
+        if (substr($name, 0, 6) === 'WeMini') {
+            self::$type = 'WeMini';
+            $name = substr($name, 6);
+        } elseif (substr($name, 0, 6) === 'WeChat') {
+            self::$type = 'WeChat';
+            $name = substr($name, 6);
+        } elseif (substr($name, 0, 5) === 'WePay') {
+            self::$type = 'WePay';
+            $name = substr($name, 5);
+        }
+        return self::instance($name, isset($arguments[0]) ? $arguments[0] : '', self::$type);
+    }
+
+}

+ 29 - 17
application/store/command/AutoRun.php

@@ -14,9 +14,6 @@
 
 namespace app\store\command;
 
-use think\console\Command;
-use think\console\Input;
-use think\console\Output;
 use think\Db;
 
 /**
@@ -24,7 +21,7 @@ use think\Db;
  * Class AutoRun
  * @package app\store\command
  */
-class AutoRun extends Command
+class AutoRun extends \think\console\Command
 {
 
     protected function configure()
@@ -34,20 +31,20 @@ class AutoRun extends Command
 
     /**
      * 业务指令执行
-     * @param Input $input
-     * @param Output $output
+     * @param \think\console\Input $input
+     * @param \think\console\Output $output
      * @throws \think\Exception
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\ModelNotFoundException
      * @throws \think\exception\DbException
      * @throws \think\exception\PDOException
      */
-    protected function execute(Input $input, Output $output)
+    protected function execute(\think\console\Input $input, \think\console\Output $output)
     {
         // 自动取消30分钟未支付的订单
         $this->autoCancelOrder();
         // 清理一天前未支付的订单
-        $this->autoCleanOrder();
+        $this->autoRemoveOrder();
         // 订单自动退款处理
         // $this->autoRefundOrder();
         // 提现自动打款处理
@@ -61,8 +58,9 @@ class AutoRun extends Command
      */
     private function autoCancelOrder()
     {
-        $where = [['create_at', '<', date('Y-m-d H:i:s', strtotime('-30 minutes'))]];
-        $count = Db::name('StoreOrder')->where(['pay_state' => '0'])->whereIn('status', ['1', '2'])->where($where)->update([
+        $datetime = $this->getDatetime('store_order_wait_time');
+        $where = [['status', 'in', ['1', '2']], ['pay_state', 'eq', '0'], ['create_at', '<', $datetime]];
+        $count = Db::name('StoreOrder')->where($where)->update([
             'status'       => '0',
             'cancel_state' => '1',
             'cancel_at'    => date('Y-m-d H:i:s'),
@@ -83,14 +81,15 @@ class AutoRun extends Command
      * @throws \think\exception\DbException
      * @throws \think\exception\PDOException
      */
-    private function autoCleanOrder()
+    private function autoRemoveOrder()
     {
-        $where = [['create_at', '<', date('Y-m-d H:i:s', strtotime('-1 day'))]];
-        $list = Db::name('StoreOrder')->where(['pay_state' => '0'])->where($where)->limit(20)->select();
-        if (count($order_nos = array_unique(array_column($list, 'order_no'))) > 0) {
-            $this->output->info("自动删除前一天已经取消的订单:\n\t" . join(',' . PHP_EOL . "\t", $order_nos));
-            Db::name('StoreOrder')->whereIn('order_no', $order_nos)->delete();
-            Db::name('StoreOrderList')->whereIn('order_no', $order_nos)->delete();
+        $datetime = $this->getDatetime('store_order_clear_time');
+        $where = [['status', 'eq', '0'], ['pay_state', 'eq', '0'], ['create_at', '<', $datetime]];
+        $list = Db::name('StoreOrder')->where($where)->limit(20)->select();
+        if (count($orderNos = array_unique(array_column($list, 'order_no'))) > 0) {
+            $this->output->info("自动删除前一天已经取消的订单:" . PHP_EOL . join(',' . PHP_EOL, $orderNos));
+            Db::name('StoreOrder')->whereIn('order_no', $orderNos)->delete();
+            Db::name('StoreOrderList')->whereIn('order_no', $orderNos)->delete();
         } else {
             $this->output->comment('没有需要自动删除前一天已经取消的订单!');
         }
@@ -172,4 +171,17 @@ class AutoRun extends Command
         }
     }
 
+    /**
+     * 获取配置时间
+     * @param string $code
+     * @return string
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    private function getDatetime($code)
+    {
+        $minutes = intval(sysconf($code) * 60);
+        return date('Y-m-d H:i:s', strtotime("-{$minutes} minutes"));
+    }
+
 }

+ 20 - 2
application/store/controller/Config.php

@@ -14,7 +14,7 @@
 
 namespace app\store\controller;
 
-use app\store\service\Extend;
+use app\store\service\ExtendService;
 use library\Controller;
 
 /**
@@ -35,7 +35,7 @@ class Config extends Controller
         $this->applyCsrfToken();
         $this->title = '商城参数配置';
         if ($this->request->isGet()) {
-            $this->query = Extend::querySmsBalance();
+            $this->query = ExtendService::querySmsBalance();
             $this->fetch();
         } else {
             foreach ($this->request->post() as $k => $v) sysconf($k, $v);
@@ -43,4 +43,22 @@ class Config extends Controller
         }
     }
 
+    /**
+     * 商城短信配置
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public function sms()
+    {
+        $this->applyCsrfToken();
+        $this->title = '商城短信配置';
+        if ($this->request->isGet()) {
+            $this->query = ExtendService::querySmsBalance();
+            $this->fetch();
+        } else {
+            foreach ($this->request->post() as $k => $v) sysconf($k, $v);
+            $this->success('商城短信配置保存成功!');
+        }
+    }
+
 }

+ 1 - 1
application/store/controller/ExpressCompany.php

@@ -95,7 +95,7 @@ class ExpressCompany extends Controller
     /**
      * 删除快递公司
      */
-    public function del()
+    public function remove()
     {
         $this->_delete($this->table);
     }

+ 1 - 1
application/store/controller/ExpressProvince.php

@@ -83,7 +83,7 @@ class ExpressProvince extends Controller
     /**
      * 删除配送省份
      */
-    public function del()
+    public function remove()
     {
         $this->applyCsrfToken();
         $this->_delete($this->table);

+ 0 - 5
application/store/controller/ExpressTemplate.php

@@ -21,11 +21,6 @@ class ExpressTemplate extends Controller
 
     /**
      * 邮费模板管理
-     * @throws \think\Exception
-     * @throws \think\db\exception\DataNotFoundException
-     * @throws \think\db\exception\ModelNotFoundException
-     * @throws \think\exception\DbException
-     * @throws \think\exception\PDOException
      */
     public function index()
     {

+ 4 - 4
application/store/controller/Goods.php

@@ -91,7 +91,7 @@ class Goods extends Controller
             ]);
             if (!empty($data)) {
                 Db::name('StoreGoodsStock')->insertAll($data);
-                \app\store\service\Goods::syncStock($post['id']);
+                \app\store\service\GoodsService::syncStock($post['id']);
                 $this->success('商品信息入库成功!');
             }
         }
@@ -104,7 +104,7 @@ class Goods extends Controller
      */
     public function add()
     {
-        $this->title = '添加商品';
+        $this->title = '添加商品信息';
         $this->isAddMode = '1';
         return $this->_form($this->table, 'form');
     }
@@ -115,7 +115,7 @@ class Goods extends Controller
      */
     public function edit()
     {
-        $this->title = '编辑商品';
+        $this->title = '编辑商品信息';
         $this->isAddMode = '0';
         return $this->_form($this->table, 'form');
     }
@@ -182,7 +182,7 @@ class Goods extends Controller
     /**
      * 删除商品信息
      */
-    public function del()
+    public function remove()
     {
         $this->_delete($this->table);
     }

+ 1 - 1
application/store/controller/GoodsCate.php

@@ -83,7 +83,7 @@ class GoodsCate extends Controller
     /**
      * 删除商品分类
      */
-    public function del()
+    public function remove()
     {
         $this->_delete($this->table);
     }

+ 2 - 1
application/store/controller/Member.php

@@ -40,7 +40,8 @@ class Member extends Controller
     public function index()
     {
         $this->title = '会员信息管理';
-        $this->_query($this->table)->like('nickname,phone')->equal('vip_level')->dateBetween('create_at')->order('id desc')->page();
+        $query = $this->_query($this->table)->like('nickname,phone')->equal('vip_level');
+        $query->dateBetween('create_at')->order('id desc')->page();
     }
 
     /**

+ 10 - 1
application/store/controller/Message.php

@@ -40,7 +40,16 @@ class Message extends Controller
     public function index()
     {
         $this->title = '短信发送管理';
-        $this->_query($this->table)->like('phone,content,result')->dateBetween('create_at')->order('id desc')->page();
+        $query = $this->_query($this->table)->like('phone,content,result');
+        $query->dateBetween('create_at')->order('id desc')->page();
+    }
+
+    /**
+     * 删除短信记录
+     */
+    public function remove()
+    {
+        $this->_delete($this->table);
     }
 
 }

+ 1 - 1
application/store/controller/Order.php

@@ -83,7 +83,7 @@ class Order extends Controller
         $this->_form($this->table);
     }
 
-    /***
+    /**
      * 快递追踪查询
      */
     public function expressQuery()

+ 2 - 2
application/store/controller/api/Notify.php

@@ -14,7 +14,7 @@
 
 namespace app\store\controller\api;
 
-use app\store\service\Order;
+use app\store\service\OrderService;
 use think\Db;
 
 /**
@@ -64,7 +64,7 @@ class Notify
             'pay_price' => $pay_price, 'pay_state' => '1', 'pay_at' => date('Y-m-d H:i:s'),
         ]);
         // 调用会员升级机制
-        Order::update($order['order_no']);
+        OrderService::update($order['order_no']);
         return $result !== false;
     }
 

+ 2 - 2
application/store/controller/api/member/Center.php

@@ -15,7 +15,7 @@
 namespace app\store\controller\api\member;
 
 use app\store\controller\api\Member;
-use app\store\service\Extend;
+use app\store\service\ExtendService;
 use think\Db;
 
 /**
@@ -79,7 +79,7 @@ class Center extends Member
             $this->error('获取短信模板失败,联系管理员配置!');
         }
         $cache = cache($cachekey);
-        if (Extend::sendSms($this->mid, $phone, str_replace('{code}', $code, $content))) {
+        if (ExtendService::sendSms($this->mid, $phone, str_replace('{code}', $code, $content))) {
             $dtime = ($cache['time'] + 120 < time()) ? 0 : (120 - time() + $cache['time']);
             $this->success('短信验证码发送成功!', ['time' => $dtime]);
         }

+ 3 - 3
application/store/controller/api/member/Order.php

@@ -15,7 +15,7 @@
 namespace app\store\controller\api\member;
 
 use app\store\controller\api\Member;
-use app\store\service\Goods;
+use app\store\service\GoodsService;
 use library\tools\Data;
 use think\Db;
 
@@ -95,7 +95,7 @@ class Order extends Member
             Db::name('StoreOrder')->insert($order);
             Db::name('StoreOrderList')->insertAll($orderList);
             // 同步商品库存及销量
-            foreach (array_unique(array_column($orderList, 'goods_id')) as $goodsId) Goods::syncStock($goodsId);
+            foreach (array_unique(array_column($orderList, 'goods_id')) as $goodsId) GoodsService::syncStock($goodsId);
             $this->success('订单创建成功,请补全收货信息后支付!', ['order' => $order]);
         } catch (\think\exception\HttpResponseException $exception) {
             throw $exception;
@@ -252,7 +252,7 @@ class Order extends Member
                 'cancel_at'    => date('Y-m-d H:i:s'),
                 'cancel_desc'  => '用户主动取消订单!',
             ]);
-            if ($result !== false && \app\store\service\Order::syncStock($order['order_no'])) {
+            if ($result !== false && \app\store\service\OrderService::syncStock($order['order_no'])) {
                 $this->success('订单取消成功!');
             }
             $this->error('订单取消失败,请稍候再试!');

+ 2 - 2
application/store/service/Express.php → application/store/service/ExpressService.php

@@ -6,10 +6,10 @@ use think\Db;
 
 /**
  * 商城邮费服务
- * Class Express
+ * Class ExpressService
  * @package app\store\service
  */
-class Express
+class ExpressService
 {
 
     /**

+ 2 - 2
application/store/service/Extend.php → application/store/service/ExtendService.php

@@ -19,10 +19,10 @@ use think\Db;
 
 /**
  * 业务扩展服务
- * Class Extend
+ * Class ExtendService
  * @package app\store\service
  */
-class Extend
+class ExtendService
 {
 
     /**

+ 2 - 2
application/store/service/Goods.php → application/store/service/GoodsService.php

@@ -18,10 +18,10 @@ use think\Db;
 
 /**
  * 商品数据管理
- * Class Goods
+ * Class GoodsService
  * @package app\store\logic
  */
-class Goods
+class GoodsService
 {
     /**
      * 同步商品库存信息

+ 3 - 3
application/store/service/Order.php → application/store/service/OrderService.php

@@ -18,10 +18,10 @@ use think\Db;
 
 /**
  * 订单服务管理器
- * Class Order
+ * Class OrderService
  * @package app\store\service
  */
-class Order
+class OrderService
 {
     /**
      * 根据订单号升级会员等级
@@ -50,7 +50,7 @@ class Order
     {
         $map = ['order_no' => $order_no];
         $goodsIds = Db::name('StoreOrderList')->where($map)->column('goods_id');
-        foreach (array_unique($goodsIds) as $goodsId) if (!Goods::syncStock($goodsId)) return false;
+        foreach (array_unique($goodsIds) as $goodsId) if (!GoodsService::syncStock($goodsId)) return false;
         return true;
     }
 

+ 33 - 64
application/store/view/config/index.html

@@ -1,71 +1,40 @@
 {extend name="admin@main"}
 
 {block name="content"}
-<form onsubmit="return false;" data-auto="true" action="{:request()->url()}" method="post" class='layui-form layui-card' autocomplete="off">
-
-    <div class="layui-card-body" style="padding-left:40px">
-
-        <fieldset>
-
-            <legend class="layui-bg-cyan">平台短信配置</legend>
-
-            {if empty($query.code)}
-            <div class="layui-code border-0">{$query.msg|default=''}</div>
-            {elseif $query.code eq 1}
-            <div class="layui-code border-0">平台剩余 {$query.num|default=0} 条可用短信,{$query.msg|default=''}</div>
-            {/if}
-
-            <div class="layui-form-item">
-                <label>
-                    <span class="color-green margin-right-10 font-s14">短信平台账号</span><span class="nowrap color-desc">Username</span>
-                    <span class="block relative">
-                        <input name="sms_zt_username" required placeholder="请输入助通短信平台账号" value="{:sysconf('sms_zt_username')}" class="layui-input">
-                    </span>
-                </label>
-                <p class="help-block">助通短信平台账号,可以联系客服获取账号与密码。</p>
-            </div>
-
-            <div class="layui-form-item">
-                <label>
-                    <span class="color-green margin-right-10 font-s14">短信平台密码</span><span class="nowrap color-desc">Password</span>
-                    <span class="block relative">
-                        <input name="sms_zt_password" required placeholder="请输入助通短信平台密码" value="{:sysconf('sms_zt_password')}" class="layui-input">
-                    </span>
-                </label>
-                <p class="help-block">助通短信平台账号,可以联系客服获取账号与密码。</p>
+<form onsubmit="return false;" data-auto="true" action="{:request()->url()}" method="post" class='layui-form layui-card' style="max-width:1000px" autocomplete="off">
+
+    <div class="layui-card-body think-box-shadow padding-left-40">
+        <div style="padding:20px 40px">
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">商城展示名称</span><span class="nowrap color-desc">StoreTitle</span>
+                <input name="store_title" required placeholder="请输入商城展示名称" value="{:sysconf('store_title')}" class="layui-input">
+                <p class="help-block">订单支付等待时长,新下订单未在此时间内容完成支付将会被自动取消</p>
+            </label>
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">未订单的支付等待时长(小时)</span><span class="nowrap color-desc">OrderWaitTime</span>
+                <input onblur="this.value=Math.abs(parseFloat(this.value)||0.00).toFixed(2)" name="store_order_wait_time" required placeholder="请输入订单支付等待时长" value="{:sysconf('store_order_wait_time')}" class="layui-input">
+                <p class="help-block">订单支付等待时长,新下订单未在此时间内容完成支付将会被自动取消</p>
+            </label>
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">已取消的订单清理时长(小时)</span><span class="nowrap color-desc">OrderClearTime</span>
+                <input onblur="this.value=Math.abs(parseFloat(this.value)||0.00).toFixed(2)" name="store_order_clear_time" required placeholder="请输入已取消订单清理时长" value="{:sysconf('store_order_clear_time')}" class="layui-input">
+                <p class="help-block">已取消订单清理时长,已取消未支付订单将在此时间点将被自动清理掉</p>
+            </label>
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">已发货订单自动确认收货时长(小时)</span><span class="nowrap color-desc">OrderConfirmTime</span>
+                <input onblur="this.value=Math.abs(parseFloat(this.value)||0.00).toFixed(2)" name="store_order_confirm_time" required placeholder="请输入已发货订单自动确认收货时长" value="{:sysconf('store_order_confirm_time')}" class="layui-input">
+                <p class="help-block">已取消订单清理时长,已取消未支付订单将在此时间点将被自动清理掉</p>
+            </label>
+
+            <div class="layui-form-item text-center margin-top-20 padding-bottom-20">
+                <button class="layui-btn" type="submit">保存配置</button>
             </div>
 
-            <div class="layui-form-item">
-                <label>
-                    <span class="color-green margin-right-10 font-s14">短信发送安全码</span><span class="nowrap color-desc">Secure</span>
-                    <span class="block relative">
-                        <input name="sms_secure" required placeholder="请输入短信发送安全码" value="{:sysconf('sms_secure')}" class="layui-input">
-                    </span>
-                </label>
-                <p class="help-block">短信发送安全码,调用接口发短信时需要传入此参数。</p>
-            </div>
-
-            <div class="layui-form-item">
-                <label>
-                    <span class="color-green margin-right-10 font-s14">注册短信模板</span><span class="nowrap color-desc">Template</span>
-                    <span class="block relative">
-                    <textarea class="layui-textarea" required name="sms_reg_template" placeholder="请输入助通短信平台密码">{:sysconf('sms_reg_template')}</textarea>
-                    </span>
-                </label>
-                <p class="help-block">会员注册短信模板,验证码变量使用 {code} 代替。</p>
-            </div>
-
-
-            <div class="layui-form-item text-center margin-top-20">
-                <div class="layui-row">
-                    <div class="layui-col-sm8 layui-col-md6">
-                        <button class="layui-btn" type="submit">保存配置</button>
-                    </div>
-                </div>
-            </div>
-
-        </fieldset>
-
+        </div>
     </div>
 </form>
-{/block}
+{/block}

+ 51 - 0
application/store/view/config/sms.html

@@ -0,0 +1,51 @@
+{extend name="admin@main"}
+
+{block name="content"}
+<form onsubmit="return false;" data-auto="true" action="{:request()->url()}" method="post" class='layui-form layui-card' style="max-width:1000px" autocomplete="off">
+
+    <div class="layui-card-body think-box-shadow padding-left-40">
+        <div style="padding:20px 40px">
+
+            <label class="layui-form-item block relative">
+                <span class="color-green margin-right-10">接口查询状态</span><span class="nowrap color-desc">UserState</span>
+                {if empty($query.code)}
+                <p class="layui-input layui-bg-gray color-red">{$query.msg|default=''}</p>
+                {elseif $query.code eq 1}
+                <p class="layui-input layui-bg-gray color-green">平台剩余 {$query.num|default=0} 条可用短信,{$query.msg|default=''}</p>
+                {else}
+                <p class="layui-input layui-bg-gray color-text">没有结果</p>
+                {/if}
+            </label>
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">短信平台账号</span><span class="nowrap color-desc">Username</span>
+                <input name="sms_zt_username" required placeholder="请输入助通短信平台账号" value="{:sysconf('sms_zt_username')}" class="layui-input">
+                <p class="help-block">助通短信平台账号,可以联系客服获取账号与密码</p>
+            </label>
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">短信平台密码</span><span class="nowrap color-desc">Password</span>
+                <input name="sms_zt_password" required placeholder="请输入助通短信平台密码" value="{:sysconf('sms_zt_password')}" class="layui-input">
+                <p class="help-block">助通短信平台账号,可以联系客服获取账号与密码</p>
+            </label>
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">短信发送安全码</span><span class="nowrap color-desc">Secure</span>
+                <input name="sms_secure" required placeholder="请输入短信发送安全码" value="{:sysconf('sms_secure')}" class="layui-input">
+                <p class="help-block">短信发送安全码,调用接口发短信时需要传入此参数</p>
+            </label>
+
+            <label class="layui-form-item margin-top-20 block relative">
+                <span class="color-green margin-right-10">注册短信模板</span><span class="nowrap color-desc">Template</span>
+                <textarea class="layui-textarea" required name="sms_reg_template" placeholder="请输入助通短信平台密码">{:sysconf('sms_reg_template')}</textarea>
+                <p class="help-block">会员注册短信模板,验证码变量使用 {code} 代替</p>
+            </label>
+
+            <div class="layui-form-item text-center margin-top-20 padding-bottom-20">
+                <button class="layui-btn" type="submit">保存配置</button>
+            </div>
+
+        </div>
+    </div>
+</form>
+{/block}

+ 56 - 56
application/store/view/express_company/index.html

@@ -4,68 +4,68 @@
 {if auth("store/express_company/add")}
 <button data-modal='{:url("add")}' data-title="添加快递" class='layui-btn layui-btn-sm layui-btn-primary'>添加快递</button>
 {/if}
-{if auth("store/express_company/del")}
-<button data-action='{:url("del")}' data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除快递</button>
+{if auth("store/express_company/remove")}
+<button data-action='{:url("remove")}' data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除快递</button>
 {/if}
 {/block}
 
 {block name="content"}
-<table class="layui-table" lay-skin="line">
-    <caption class="margin-bottom-10 text-left">{include file='express_company/index_search'}</caption>
-    <!--{notempty name='list'}-->
-    <thead>
-    <tr>
-        <th class='list-table-check-td think-checkbox'>
-            <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
-        </th>
-        <th class='list-table-sort-td'>
-            <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
-        </th>
-        <th class='text-left nowrap'>快递名称</th>
-        <th class='text-left nowrap'>快递编码</th>
-        <th class="text-center">记录状态</th>
-        <th class="text-center">创建时间</th>
-        <th></th>
-    </tr>
-    </thead>
-    <!--{/notempty}-->
-    <tbody>
-    <!--{foreach $list as $key=>$vo}-->
-    <tr data-dbclick>
-        <td class='list-table-check-td think-checkbox'>
-            <input class="list-check-box" value='{$vo.id}' type='checkbox'>
-        </td>
-        <td class='list-table-sort-td'>
-            <input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input">
-        </td>
-        <td class='text-left nowrap'>{$vo.express_title|default=''}</td>
-        <td class='text-left nowrap'>{$vo.express_code|default=''}</td>
-        <td class='text-center nowrap'>
-            {eq name='vo.status' value='0'}<span class="layui-badge">已禁用</span>{else}<span class="layui-badge layui-bg-green">使用中</span>{/eq}<br>
-        </td>
-        <td class='text-center nowrap'>{$vo.create_at|format_datetime}</td>
-        <td class='text-left nowrap'>
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='express_company/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class='list-table-sort-td'>
+                <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
+            </th>
+            <th class='text-left nowrap'>快递名称</th>
+            <th class='text-left nowrap'>快递编码</th>
+            <th class="text-center">记录状态</th>
+            <th class="text-center">创建时间</th>
+            <th></th>
+        </tr>
+        </thead>
+        {/notempty}
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr data-dbclick>
+            <td class='list-table-check-td think-checkbox'>
+                <input class="list-check-box" value='{$vo.id}' type='checkbox'>
+            </td>
+            <td class='list-table-sort-td'>
+                <input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input">
+            </td>
+            <td class='text-left nowrap'>{$vo.express_title|default=''}</td>
+            <td class='text-left nowrap'>{$vo.express_code|default=''}</td>
+            <td class='text-center nowrap'>
+                {eq name='vo.status' value='0'}<span class="layui-badge">已禁用</span>{else}<span class="layui-badge layui-bg-green">使用中</span>{/eq}<br>
+            </td>
+            <td class='text-center nowrap'>{$vo.create_at|format_datetime}</td>
+            <td class='text-left nowrap'>
 
-            {if auth("store/express_company/edit")}
-            <a data-dbclick data-title="编辑快递" class="layui-btn layui-btn-xs" data-modal='{:url("edit")}?id={$vo.id}'>编 辑</a>
-            {/if}
+                {if auth("store/express_company/edit")}
+                <a data-dbclick data-title="编辑快递" class="layui-btn layui-btn-xs" data-modal='{:url("edit")}?id={$vo.id}'>编 辑</a>
+                {/if}
 
-            {if $vo.status eq 1 and auth("store/express_company/forbid")}
-            <a class="layui-btn layui-btn-xs layui-btn-warm" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0">禁 用</a>
-            {elseif auth("store/express_company/resume")}
-            <a class="layui-btn layui-btn-xs layui-btn-warm" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1">启 用</a>
-            {/if}
+                {if $vo.status eq 1 and auth("store/express_company/forbid")}
+                <a class="layui-btn layui-btn-xs layui-btn-warm" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0">禁 用</a>
+                {elseif auth("store/express_company/resume")}
+                <a class="layui-btn layui-btn-xs layui-btn-warm" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1">启 用</a>
+                {/if}
 
-            {if auth("store/express_company/del")}
-            <a class="layui-btn layui-btn-xs layui-btn-danger" data-confirm="确定要删除数据吗?" data-action="{:url('del')}" data-value="id#{$vo.id}">删 除</a>
-            {/if}
-
-        </td>
-    </tr>
-    <!--{/foreach}-->
-    </tbody>
-</table>
-
-{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+                {if auth("store/express_company/remove")}
+                <a class="layui-btn layui-btn-xs layui-btn-danger" data-confirm="确定要删除数据吗?" data-action="{:url('remove')}" data-value="id#{$vo.id}">删 除</a>
+                {/if}
 
+            </td>
+        </tr>
+        {/foreach}
+        </tbody>
+    </table>
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+</div>
 {/block}

+ 55 - 56
application/store/view/express_province/index.html

@@ -6,67 +6,66 @@
 <button data-modal='{:url("add")}' data-title="添加省份" class='layui-btn layui-btn-sm layui-btn-primary'>添加省份</button>
 <!--{/if}-->
 
-<!--{if auth("store/express_province/del")}-->
-<button data-action='{:url("del")}' data-csrf="{:systoken('store/express_province/del')}" data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除省份</button>
-<!--{/if}-->
+{if auth("store/express_province/remove")}
+<button data-action='{:url("remove")}' data-csrf="{:systoken('store/express_province/remove')}" data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除省份</button>
+{/if}
 
 {/block}
 
 {block name="content"}
-<table class="layui-table" lay-skin="line">
-    <caption class="margin-bottom-10 text-left">{include file='express_province/index_search'}</caption>
-    {notempty name='list'}
-    <thead>
-    <tr>
-        <th class='list-table-check-td think-checkbox'>
-            <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
-        </th>
-        <th class='list-table-sort-td'>
-            <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
-        </th>
-        <th>省份名称</th>
-        <th>创建时间</th>
-        <th class="text-center">使用状态</th>
-        <th></th>
-    </tr>
-    </thead>
-    <tbody>
-    {foreach $list as $key=>$vo}
-    <tr data-dbclick>
-        <td class='list-table-check-td think-checkbox'>
-            <input class="list-check-box" value='{$vo.id}' type='checkbox'>
-        </td>
-        <td class='list-table-sort-td padding-left-0 padding-right-0'>
-            <input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input">
-        </td>
-        <td class="nowrap">{$vo.title|default='--'}</td>
-        <td class="nowrap">{$vo.create_at|format_datetime}</td>
-        <td class='text-center nowrap'>
-            {eq name='vo.status' value='0'}<span class="layui-badge">已禁用</span>{else}<span class="layui-badge layui-bg-green">使用中</span>{/eq}
-        </td>
-        <td class='text-center nowrap notselect'>
-
-            {if auth("store/express_province/edit")}
-            <a data-dbclick class="layui-btn layui-btn-xs" data-title="编辑区域" data-modal='{:url("store/express_province/edit")}?id={$vo.id}'>编 辑</a>
-            {/if}
-
-            {if $vo.status eq 1 and auth("store/express_province/forbid")}
-            <a class="layui-btn layui-btn-warm layui-btn-xs" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0" data-csrf="{:systoken('store/express_province/forbid')}">禁 用</a>
-            {elseif auth("store/express_province/resume")}
-            <a class="layui-btn layui-btn-warm layui-btn-xs" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1" data-csrf="{:systoken('store/express_province/resume')}">启 用</a>
-            {/if}
-
-            {if auth("store/express_province/del")}
-            <a class="layui-btn layui-btn-danger layui-btn-xs" data-confirm="确定要删除数据吗?" data-action="{:url('del')}" data-value="id#{$vo.id}" data-csrf="{:systoken('store/express_province/del')}">删 除</a>
-            {/if}
-        </td>
-    </tr>
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='express_province/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class='list-table-sort-td'>
+                <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
+            </th>
+            <th>省份名称</th>
+            <th>创建时间</th>
+            <th class="text-center">使用状态</th>
+            <th></th>
+        </tr>
+        </thead>
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr data-dbclick>
+            <td class='list-table-check-td think-checkbox'>
+                <input class="list-check-box" value='{$vo.id}' type='checkbox'>
+            </td>
+            <td class='list-table-sort-td padding-left-0 padding-right-0'>
+                <input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input">
+            </td>
+            <td class="nowrap">{$vo.title|default='--'}</td>
+            <td class="nowrap">{$vo.create_at|format_datetime}</td>
+            <td class='text-center nowrap'>
+                {eq name='vo.status' value='0'}<span class="layui-badge">已禁用</span>{else}<span class="layui-badge layui-bg-green">使用中</span>{/eq}
+            </td>
+            <td class='text-center nowrap notselect'>
 
-    {/foreach}
-    </tbody>
-    {/notempty}
-</table>
+                {if auth("store/express_province/edit")}
+                <a data-dbclick class="layui-btn layui-btn-xs" data-title="编辑区域" data-modal='{:url("store/express_province/edit")}?id={$vo.id}'>编 辑</a>
+                {/if}
 
-{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+                {if $vo.status eq 1 and auth("store/express_province/forbid")}
+                <a class="layui-btn layui-btn-warm layui-btn-xs" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0" data-csrf="{:systoken('store/express_province/forbid')}">禁 用</a>
+                {elseif auth("store/express_province/resume")}
+                <a class="layui-btn layui-btn-warm layui-btn-xs" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1" data-csrf="{:systoken('store/express_province/resume')}">启 用</a>
+                {/if}
 
+                {if auth("store/express_province/remove")}
+                <a class="layui-btn layui-btn-danger layui-btn-xs" data-confirm="确定要删除数据吗?" data-action="{:url('remove')}" data-value="id#{$vo.id}" data-csrf="{:systoken('store/express_province/remove')}">删 除</a>
+                {/if}
+            </td>
+        </tr>
+        {/foreach}
+        </tbody>
+        {/notempty}
+    </table>
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+</div>
 {/block}

+ 2 - 2
application/store/view/express_template/index.html

@@ -1,9 +1,9 @@
 {extend name="admin@main"}
 
 {block name="content"}
-<form onsubmit="return false;" data-auto="true" action="{:request()->url()}" method="post" class='layui-form layui-card' autocomplete="off">
+<form onsubmit="return false;" data-auto="true" action="{:request()->url()}" method="post" class='layui-form layui-card' style="max-width:1000px" autocomplete="off">
 
-    <div class="layui-card-body" style="padding-left:40px">
+    <div class="layui-card-body think-box-shadow padding-bottom-20" style="padding-left:40px">
 
         <div data-item-container>
             {foreach $list as $index=>$item}

+ 107 - 198
application/store/view/goods/form.html

@@ -1,35 +1,40 @@
 {extend name='admin@main'}
 
 {block name="content"}
+
+{include file='store@goods/form_style'}
+
 <form onsubmit="return false;" id="GoodsForm" data-auto="true" method="post" class='layui-form layui-card' autocomplete="off">
 
-    <div class="layui-card-body padding-left-40">
+    <div class="layui-card-body think-box-shadow padding-left-40">
 
-        <div class="layui-form-item block relative">
-            <h3 class="color-green"><sup class='color-red font-s14 absolute' style="margin-left:-10px">*</sup>商品分类</h3>
-            <select class="layui-select" name="cate_id" lay-search>
-                {foreach $cates as $cate}
-                {if isset($vo.cate_id) and $vo.cate_id eq $cate.id}
-                <option selected value="{$cate.id}">{$cate.title|default=''}</option>
-                {else}
-                <option value="{$cate.id}">{$cate.title|default=''}</option>
-                {/if}
-                {/foreach}
-            </select>
+        <div class="layui-form-item layui-row layui-col-space15">
+            <label class="layui-col-xs3 relative">
+                <span class="color-green">商品分类</span>
+                <select class="layui-select" required name="cate_id" lay-search>
+                    {foreach $cates as $cate}
+                    {if isset($vo.cate_id) and $vo.cate_id eq $cate.id}
+                    <option selected value="{$cate.id}">{$cate.id} - {$cate.title|default=''}</option>
+                    {else}
+                    <option value="{$cate.id}">{$cate.id} - {$cate.title|default=''}</option>
+                    {/if}
+                    {/foreach}
+                </select>
+            </label>
+            <label class="layui-col-xs9 relative">
+                <span class="color-green">商品名称</span>
+                <input name="title" required class="layui-input" placeholder="请输入商品名称" value="{$vo.title|default=''}">
+            </label>
         </div>
 
-        <div class="layui-form-item block relative">
-            <h3 class="color-green"><sup class='color-red font-s14 absolute' style="margin-left:-10px">*</sup>商品名称</h3>
-            <input name="title" required class="layui-input" placeholder="请输入商品名称" value="{$vo.title|default=''}">
-        </div>
 
         <div class="layui-form-item">
-            <h3 class="color-green"><sup class='color-red font-s14 absolute' style="margin-left:-10px">*</sup>商品图片</h3>
+            <span class="color-green label-required-prev">商品LOGO及轮播展示图片</span>
             <table class="layui-table">
                 <thead>
                 <tr>
                     <th class="text-center">LOGO</th>
-                    <th class="text-left">商品图片</th>
+                    <th class="text-left">展示图片</th>
                 </tr>
                 <tr>
                     <td width="90px" class="text-center"><input name="logo" type="hidden" value="{$vo.logo|default=''}"></td>
@@ -40,42 +45,25 @@
             <script>$('[name="logo"]').uploadOneImage(), $('[name="image"]').uploadMultipleImage()</script>
         </div>
 
-        <fieldset class="margin-bottom-15">
-            <legend>快递运费及分成比例</legend>
-            <div>
-                <div class="layui-form-item block relative">
-                    <div class="inline-block">
-                        <div class="layui-unselect layui-form-radio">
-                            <label>快递运费 <input onblur="this.value=(parseFloat(this.value)||0).toFixed(2)" name="price_express" value="{$vo.price_express|default='0.00'}" class="inline-block text-center font-s12 inner-input"> 元,</label>
-                            <label>分成比例 <input onblur="this.value=(parseFloat(this.value)||0).toFixed(4)" name="price_rate" value="{$vo.price_rate|default='0.00'}" class="inline-block text-center font-s12 inner-input"> %;</label>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </fieldset>
-
         <div class="layui-form-item">
-            <h3 class="color-green">
-                <sup class='color-red font-s14 absolute' style="margin-left:-10px">*</sup>
-                商品规格<span class="color-desc font-s10">(最多支持3个规格分组,每个分组最多支持5个规格属性;仅添加商品时可编辑内容)</span>
-            </h3>
+            <span class="color-green label-required-prev">商品规格及商品SKU绑定<span class="color-red font-s12">(规格填写后不允许再次修改)</span></span>
             <div ng-repeat="x in specs track by $index" style="display:none" class="margin-bottom-10" ng-class="{true:'layui-show'}[isAddMode&&specs.length>0]">
                 <div class="goods-spec-box padding-10 margin-0 relative" style="background:#ddd">
-                    <span class="text-center goods-spec-title">名称</span>
-                    <input ng-model="x.name" required placeholder="请输入分组">
-                    <div class="fa-pull-right">
+                    <span class="text-center goods-spec-title">分组</span>
+                    <label class="label-required-null inline-block"><input ng-blur="x.name=trimSpace(x.name)" ng-model="x.name" required placeholder="请输入分组名称"></label>
+                    <div class="pull-right">
+                        <a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" ng-click="addSpecVal(x.list)">增加</a>
                         <a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" ng-class="{false:'layui-bg-gray'}[$index>0]" ng-click="upSpecRow(specs,$index)">上移</a>
                         <a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" ng-class="{false:'layui-bg-gray'}[$index<specs.length-1]" ng-click="dnSpecRow(specs,$index)">下移</a>
-                        <a ng-if="specs.length>1" class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" ng-click="delSpecRow(specs,$index)">删除</a>
+                        <a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" ng-click="delSpecRow(specs,$index)" ng-if="specs.length>1">删除</a>
                     </div>
                 </div>
-                <div class="block padding-10 layui-bg-gray" ng-if="x.list && x.list.length > 0">
-                    <label class="goods-spec-box inline-block nowrap margin-bottom-0" ng-repeat="xx in x.list">
+                <div class="goods-spec-box padding-10 margin-0 layui-bg-gray block relative" ng-if="x.list && x.list.length > 0">
+                    <label class="label-required-null inline-block margin-right-10 margin-bottom-5 relative nowrap" ng-repeat="xx in x.list">
                         <input type="checkbox" lay-ignore ng-model="xx.check" ng-click="xx.check=checkListChecked(x.list,$event.target.checked)">
-                        <input type="text" ng-model="xx.name" ng-keyup="xx.name=$event.target.value" required placeholder="请输入规格">
+                        <input type="text" ng-blur="xx.name=trimSpace(xx.name)" ng-model="xx.name" ng-keyup="xx.name=$event.target.value" required placeholder="请输入规格">
                         <a ng-if="x.list.length>1" ng-click="x.list=delSpecVal(x.list,$index)" class="layui-icon layui-icon-close font-s12 goods-spec-close"></a>
                     </label>
-                    <a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" ng-if="x.list.length<5" ng-click="addSpecVal(x.list)">增加</a>
                 </div>
             </div>
             <a ng-if="isAddMode&&specs.length<3" class="layui-btn layui-btn-sm layui-btn-primary" ng-click="addSpecRow(specs)">增加分组</a>
@@ -83,10 +71,11 @@
                 <thead>
                 <tr>
                     <th ng-repeat="x in specsTreeNava track by $index" class="nowrap" ng-bind="x"></th>
-                    <th width="10%" class="text-center nowrap">快递数量 <a ng-click="batchSet('express',0)" data-tips-text="批量设置" class="layui-icon">&#xe63c;</a></th>
-                    <th width="10%" class="text-center nowrap">虚拟销量 <a ng-click="batchSet('virtual',0)" data-tips-text="批量设置" class="layui-icon">&#xe63c;</a></th>
+                    <th width="10%" class="text-center nowrap">商品SKU <a ng-click="batchSet('sku',0)" data-tips-text="批量设置" class="layui-icon">&#xe63c;</a></th>
                     <th width="10%" class="text-center nowrap">市场价格 <a ng-click="batchSet('market',2)" data-tips-text="批量设置" class="layui-icon">&#xe63c;</a></th>
                     <th width="10%" class="text-center nowrap">销售价格 <a ng-click="batchSet('selling',2)" data-tips-text="批量设置" class="layui-icon">&#xe63c;</a></th>
+                    <th width="10%" class="text-center nowrap">虚拟销量 <a ng-click="batchSet('virtual',0)" data-tips-text="批量设置" class="layui-icon">&#xe63c;</a></th>
+                    <th width="10%" class="text-center nowrap">快递计件 <a ng-click="batchSet('express',0)" data-tips-text="批量设置" class="layui-icon">&#xe63c;</a></th>
                     <th width="10%" class="text-center nowrap">销售状态</th>
                 </tr>
                 </thead>
@@ -95,22 +84,27 @@
                     <td class="layui-bg-gray" ng-if="td.show" rowspan="{{td.span}}" ng-repeat="td in rows" ng-bind="td.name"></td>
                     <td class="padding-0">
                         <label class="padding-0 margin-0">
-                            <input ng-blur="rows[0].express=setValue(rows[0].key,'express',$event.target.value,'(parseFloat(_)||0).toFixed(0)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].express">
+                            <input ng-blur="rows[0].sku=setValue(rows[0].key,'sku',$event.target.value,'(parseFloat(_)||0).toFixed(0)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].sku">
                         </label>
                     </td>
                     <td class="padding-0">
                         <label class="padding-0 margin-0">
-                            <input ng-blur="rows[0].virtual=setValue(rows[0].key,'virtual',$event.target.value,'(parseInt(_)||0)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].virtual">
+                            <input ng-blur="rows[0].market=setValue(rows[0].key,'market',$event.target.value,'(parseFloat(_)||0).toFixed(2)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].market">
                         </label>
                     </td>
                     <td class="padding-0">
                         <label class="padding-0 margin-0">
-                            <input ng-blur="rows[0].market=setValue(rows[0].key,'market',$event.target.value,'(parseFloat(_)||0).toFixed(2)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].market">
+                            <input ng-blur="rows[0].selling=setValue(rows[0].key,'selling',$event.target.value,'(parseFloat(_)||0).toFixed(2)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].selling">
                         </label>
                     </td>
                     <td class="padding-0">
                         <label class="padding-0 margin-0">
-                            <input ng-blur="rows[0].selling=setValue(rows[0].key,'selling',$event.target.value,'(parseFloat(_)||0).toFixed(2)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].selling">
+                            <input ng-blur="rows[0].virtual=setValue(rows[0].key,'virtual',$event.target.value,'(parseInt(_)||0)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].virtual">
+                        </label>
+                    </td>
+                    <td class="padding-0">
+                        <label class="padding-0 margin-0">
+                            <input ng-blur="rows[0].express=setValue(rows[0].key,'express',$event.target.value,'(parseInt(_)||0)')" class="layui-input border-0 padding-left-0 text-center" ng-model="rows[0].express">
                         </label>
                     </td>
                     <td class="text-center layui-bg-gray">
@@ -119,12 +113,13 @@
                 </tr>
                 </tbody>
             </table>
+            <p class="color-desc">请从仓储平台获取商品SKU与商品条码,请注意尽量不要重复,也不能产生订单后再修改!</p>
             <textarea class="layui-textarea layui-hide" name="specs">{{specs}}</textarea>
             <textarea class="layui-textarea layui-hide" name="lists">{{specsTreeData}}</textarea>
         </div>
 
         <div class="layui-form-item block">
-            <h3 class="color-green"><sup class='color-red font-s14 absolute' style="margin-left:-10px">*</sup>商品内容</h3>
+            <span class="label-required-prev color-green">商品详细内容</span>
             <textarea name="content">{$vo.content|default=''|raw}</textarea>
         </div>
 
@@ -136,110 +131,22 @@
 
     </div>
 </form>
-
-<style>
-
-    .inner-input {
-        width: 80px;
-        height: 14px;
-        padding: 1px 5px;
-        line-height: 12px;
-    }
-
-    .goods-spec-box {
-        position: relative;
-        margin: 0 10px 10px 0;
-        vertical-align: middle;
-    }
-
-    .goods-spec-title {
-        z-index: 2;
-        width: 40px;
-        color: #fff;
-        height: 28px;
-        position: absolute;
-        background: #999;
-        line-height: 28px;
-    }
-
-    .goods-spec-close {
-        right: 8px;
-        z-index: 2;
-        line-height: 28px;
-        position: absolute;
-        display: inline-block
-    }
-
-    .goods-spec-btn {
-        height: 28px;
-        margin-left: 5px !important;
-        line-height: 26px !important;
-    }
-
-    .goods-spec-box input {
-        z-index: 1;
-        width: 120px;
-        position: relative;
-        border: 1px solid #999;
-        padding: 5px 0 5px 45px;
-        display: inline-block !important;
-    }
-
-    .goods-spec-box input[type=checkbox] {
-        z-index: 2;
-        width: 40px;
-        height: 28px;
-        border: none;
-        cursor: pointer;
-        appearance: none;
-        position: absolute;
-        -webkit-appearance: none;
-    }
-
-    .goods-spec-box input[type=checkbox]:before {
-        top: 1px;
-        left: 1px;
-        width: 40px;
-        height: 26px;
-        content: ' ';
-        position: absolute;
-        background: #c9c9c9;
-    }
-
-    .goods-spec-box input[type=checkbox]:after {
-        top: 1px;
-        left: 1px;
-        color: #999;
-        width: 40px;
-        height: 26px;
-        content: '\e63f';
-        font-size: 16px;
-        line-height: 26px;
-        position: absolute;
-        text-align: center;
-        font-family: 'layui-icon';
-    }
-
-    .goods-spec-box input[type=checkbox]:checked:after {
-        color: #333;
-        content: '\e605';
-    }
-</style>
-
 {/block}
 
 {block name='script'}
+<textarea class="layui-hide" id="goods-specs">{$vo.specs|raw|default=''}</textarea>
+<textarea class="layui-hide" id="goods-value">{$defaultValues|raw|default=''}</textarea>
 <script>
     window.form.render();
     require(['ckeditor', 'angular'], function () {
         window.createEditor('[name="content"]', {height: 500});
-        let app = angular.module("GoodsForm", []).run(callback);
+        var app = angular.module("GoodsForm", []).run(callback);
         angular.bootstrap(document.getElementById(app.name), [app.name]);
 
         function callback($rootScope) {
             $rootScope.isAddMode = parseInt('{$isAddMode|default=0}');
-            let defaultValues = angular.fromJson('{$defaultValues|raw|default=0}') || {};
-            $rootScope.specs = angular.fromJson('{$vo.specs|raw|default=0}') || [{name: '默认分组', list: [{name: '默认规格', check: true}]}];
+            $rootScope.maps = JSON.parse(angular.element('#goods-value').val() || '[]') || {};
+            $rootScope.specs = JSON.parse(angular.element('#goods-specs').val() || '[{"name":"默认分组","list":[{"name":"默认规格","check":true}]}]');
             // 批量设置数值
             $rootScope.batchSet = function (type, fixed) {
                 layer.prompt({title: '请输入数值', formType: 0}, function (value, index) {
@@ -260,72 +167,74 @@
             };
             // 设置默认值
             $rootScope.setValue = function (key, type, value, call) {
-                defaultValues[key] || (defaultValues[key] = {});
-                return defaultValues[key][type] = eval(call.replace('_', "'" + value + "'"));
+                $rootScope.maps[key] || ($rootScope.maps[key] = {});
+                return $rootScope.maps[key][type] = eval(call.replace('_', "'" + value + "'"));
             };
             // 读取默认值
-            let getValue = function (key, callback) {
-                if (typeof callback === 'function') return callback(defaultValues[key] || {});
+            $rootScope.getValue = function (key, callback) {
+                if (typeof callback === 'function') {
+                    return callback($rootScope.maps[key] || {});
+                }
+                return {};
+            };
+            // 去除空白字符
+            $rootScope.trimSpace = function (value) {
+                return (value + '').replace(/\s*/ig, '');
             };
             // 生成交叉表格数据
             $rootScope.specsTreeData = [];
             $rootScope.specsTreeNava = [];
             // 当前商品规格发生变化时重新计算规格列表
             $rootScope.$watch('specs', function () {
-                let list = [], nava = [], table = [[]];
-                let data = angular.fromJson(angular.toJson($rootScope.specs));
+                var data = $rootScope.specs, list = [], navs = [], table = [[]];
                 // 过滤无效记录
-                for (let o of data) {
-                    let tmp = [];
-                    for (let x of o.list) (x.check && typeof x.name === 'string' && x.name.length > 0) && tmp.push(x);
-                    if (tmp.length > 0) for (let x of tmp) x.group = o.name, x.span = 1, x.show = true;
-                    if (tmp.length > 0) list.push(tmp), nava.push(o.name);
-                    $rootScope.specsTreeNava = nava;
+                for (var i in data) {
+                    var tmp = [];
+                    for (var j in data[i].list) if (data[i].list[j].check && data[i].list[j].name.length > 0) {
+                        data[i].list[j].span = 1, data[i].list[j].show = true, data[i].list[j].group = data[i].name;
+                        tmp.push(data[i].list[j]);
+                    }
+                    list.push(tmp), navs.push(data[i].name);
                 }
+                $rootScope.specsTreeNava = navs;
                 // 表格交叉
-                for (let i in list) {
-                    let temp = [];
-                    for (let j in table) for (let k in list[i]) temp.push(table[j].concat(list[i][k]));
-                    table = temp;
+                for (var i in list) {
+                    var tmp = [];
+                    for (var j in table) for (var k in list[i]) tmp.push(table[j].concat(list[i][k]));
+                    table = tmp;
                 }
                 // 表格合并
                 list = angular.fromJson(angular.toJson(table));
-                for (let row in list) {
-                    let key = [], _key = '';
-                    for (let td in list[row]) key.push(list[row][td].group + '::' + list[row][td].name);
-                    for (let td in list[row]) {
-                        if (_key.length === 0) {
-                            list[row][0].key = _key = key.join(';;');
-                            list[row][0].express = getValue(_key, function (data) {
-                                return data.express || '1';
-                            });
-                            list[row][0].virtual = getValue(_key, function (data) {
-                                return data.virtual || '0';
-                            });
-                            list[row][0].market = getValue(_key, function (data) {
-                                return data.market || '0.00';
-                            });
-                            list[row][0].selling = getValue(_key, function (data) {
-                                return data.selling || '0.00';
-                            });
-
-                            list[row][0].status = getValue(_key, function (data) {
-                                return !!(typeof data.status !== 'undefined' ? data.status : true);
-                            });
-                        }
-                        // 表格TD处理
-                        for (let dow = 1 + parseInt(row); dow < list.length; dow++)
-                            if (list[row][td].name === list[dow][td].name) {
-                                list[row][td].span++;
-                                list[dow][td].show = false;
-                            } else break;
+                for (var i in list) {
+                    var key = [], _key = '';
+                    for (var td in list[i]) key.push(list[i][td].group + '::' + list[i][td].name);
+                    for (var td in list[i]) if (_key.length === 0) {
+                        list[i][0].key = _key = key.join(';;');
+                        list[i][0].sku = $rootScope.getValue(_key, function (data) {
+                            return data.sku || '0';
+                        });
+                        list[i][0].virtual = $rootScope.getValue(_key, function (data) {
+                            return data.virtual || '0';
+                        });
+                        list[i][0].express = $rootScope.getValue(_key, function (data) {
+                            return data.express || '1';
+                        });
+                        list[i][0].market = $rootScope.getValue(_key, function (data) {
+                            return data.market || '0.00';
+                        });
+                        list[i][0].selling = $rootScope.getValue(_key, function (data) {
+                            return data.selling || '0.00';
+                        });
+                        list[i][0].status = $rootScope.getValue(_key, function (data) {
+                            return !!(typeof data.status !== 'undefined' ? data.status : true);
+                        });
                     }
                 }
                 $rootScope.specsTreeData = list;
             }, true);
             // 判断规则是否能取消选择
             $rootScope.checkListChecked = function (list, check) {
-                for (let o of  list) if (o.check) return check;
+                for (var i in list) if (list[i].check) return check;
                 return true;
             };
             // 增加整行规格分组
@@ -334,9 +243,9 @@
             };
             // 下移整行规格分组
             $rootScope.dnSpecRow = function (data, $index) {
-                let tmp = [], cur = data[$index];
+                var tmp = [], cur = data[$index];
                 if ($index > data.length - 2) return false;
-                for (let i in data) {
+                for (var i in data) {
                     (parseInt(i) !== parseInt($index)) && tmp.push(data[i]);
                     (parseInt(i) === parseInt($index) + 1) && tmp.push(cur);
                 }
@@ -344,9 +253,9 @@
             };
             // 上移整行规格分组
             $rootScope.upSpecRow = function (data, $index) {
-                let tmp = [], cur = data[$index];
+                var tmp = [], cur = data[$index];
                 if ($index < 1) return false;
-                for (let i in data) {
+                for (var i in data) {
                     (parseInt(i) === parseInt($index) - 1) && tmp.push(cur);
                     (parseInt(i) !== parseInt($index)) && tmp.push(data[i]);
                 }
@@ -354,8 +263,8 @@
             };
             // 移除整行规格分组
             $rootScope.delSpecRow = function (data, $index) {
-                let tmp = [];
-                for (let i in data) if (parseInt(i) !== parseInt($index)) tmp.push(data[i]);
+                var tmp = [];
+                for (var i in data) if (parseInt(i) !== parseInt($index)) tmp.push(data[i]);
                 return $rootScope.specs = tmp;
             };
             // 增加分组的属性
@@ -364,8 +273,8 @@
             };
             // 移除分组的属性
             $rootScope.delSpecVal = function (data, $index) {
-                let temp = [];
-                for (let i in data) if (parseInt(i) !== parseInt($index)) temp.push(data[i]);
+                var temp = [];
+                for (var i in data) if (parseInt(i) !== parseInt($index)) temp.push(data[i]);
                 return temp;
             };
         }

+ 87 - 0
application/store/view/goods/form_style.html

@@ -0,0 +1,87 @@
+<style>
+    .inner-input {
+        width: 80px;
+        height: 14px;
+        padding: 1px 5px;
+        line-height: 12px;
+    }
+
+    .goods-spec-box {
+        position: relative;
+        margin: 0 10px 10px 0;
+        vertical-align: middle;
+    }
+
+    .goods-spec-title {
+        z-index: 2;
+        width: 40px;
+        color: #fff;
+        height: 28px;
+        position: absolute;
+        background: #999;
+        line-height: 28px;
+    }
+
+    .goods-spec-close {
+        right: 8px;
+        z-index: 2;
+        line-height: 28px;
+        position: absolute;
+        display: inline-block
+    }
+
+    .goods-spec-btn {
+        height: 28px;
+        margin-left: 5px !important;
+        line-height: 26px !important;
+    }
+
+    .goods-spec-box input {
+        z-index: 1;
+        width: 120px;
+        position: relative;
+        border: 1px solid #999;
+        padding: 5px 0 5px 45px;
+        display: inline-block !important;
+    }
+
+    .goods-spec-box input[type=checkbox] {
+        z-index: 2;
+        width: 40px;
+        height: 28px;
+        border: none;
+        cursor: pointer;
+        appearance: none;
+        position: absolute;
+        -webkit-appearance: none;
+    }
+
+    .goods-spec-box input[type=checkbox]:before {
+        top: 1px;
+        left: 1px;
+        width: 40px;
+        height: 26px;
+        content: ' ';
+        position: absolute;
+        background: #c9c9c9;
+    }
+
+    .goods-spec-box input[type=checkbox]:after {
+        top: 1px;
+        left: 1px;
+        color: #999;
+        width: 40px;
+        height: 26px;
+        content: '\e63f';
+        font-size: 16px;
+        line-height: 26px;
+        position: absolute;
+        text-align: center;
+        font-family: 'layui-icon';
+    }
+
+    .goods-spec-box input[type=checkbox]:checked:after {
+        color: #333;
+        content: '\e605';
+    }
+</style>

+ 97 - 101
application/store/view/goods/index.html

@@ -4,119 +4,115 @@
 <!--{if auth("store/goods/add")}-->
 <button data-open='{:url("add")}' data-title="添加商品" class='layui-btn layui-btn-sm layui-btn-primary'>添加商品</button>
 <!--{/if}-->
-<!--{if auth("store/goods/del")}-->
-<button data-action='{:url("del")}' data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除商品</button>
+<!--{if auth("store/goods/remove")}-->
+<button data-action='{:url("remove")}' data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除商品</button>
 <!--{/if}-->
 {/block}
 
 {block name="content"}
-<table class="layui-table" lay-skin="line">
-    <caption class="margin-bottom-10 text-left">{include file='goods/index_search'}</caption>
-    <!--{notempty name='list'}-->
-    <thead>
-    <tr>
-        <th class='list-table-check-td think-checkbox'>
-            <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
-        </th>
-        <th class='list-table-sort-td'>
-            <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
-        </th>
-        <th class='text-left nowrap' width="10%">商品信息</th>
-        <th class='text-left nowrap' width="10%">商品状态</th>
-        <th class='text-left nowrap padding-0 relative' style="min-width:400px">
-            <div class="layui-row" style="line-height:28px">
-                <div class="layui-col-xs6 text-center layui-elip">商品规格</div>
-                <div class="layui-col-xs3 text-center layui-elip">市价 / 售价</div>
-                <div class="layui-col-xs3 text-center layui-elip">库存 / 销量</div>
-            </div>
-        </th>
-        <th></th>
-    </tr>
-    </thead>
-    <!--{/notempty}-->
-    <tbody>
-    <!--{foreach $list as $key=>$vo}-->
-    <tr data-dbclick>
-        <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
-        <td class='list-table-sort-td'><input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input"></td>
-        <td class='text-left nowrap'>
-            {notempty name='vo.logo'}
-            <img data-tips-image style="width:60px;height:60px" src="{$vo.logo|default=''}" class="margin-right-5 text-top">
-            {/notempty}
-            <div class="inline-block">
-                商品编号:{$vo.id|default='--'}<br>
-                所属分类:{$vo.cate.title|default='--'}<br>
-                商品名称:{$vo.title|default='--'}<br>
-            </div>
-        </td>
-        <td class='text-left nowrap'>
-            快递费用:{$vo.price_express|default='0.00'} 元<br>
-            分成比例:{$vo.price_rate+0} %<br>
-            销售状态:{eq name='vo.status' value='0'}<span class="layui-badge">已下架</span>{else}<span class="layui-badge layui-bg-green">销售中</span>{/eq}<br>
-        </td>
-        <td class='text-left nowrap padding-0 relative'>
-            <div style="overflow:auto;max-height:68px;padding:5px 0">
-                {foreach $vo.list as $goods}
-                <div class="layui-row" style="line-height:23px">
-                    <div class="layui-col-xs6 text-center layui-elip font-s10">{:str_replace(['::',';;'],[':',';'],$goods.goods_spec)}</div>
-                    <div class="layui-col-xs3 text-center layui-elip color-blue font-s10">{$goods.price_market+0} / {$goods.price_selling+0}</div>
-                    <div class="layui-col-xs3 text-center layui-elip color-blue font-s10">{$goods.number_stock+0} / {$goods.number_sales+0}</div>
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='goods/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class='list-table-sort-td'>
+                <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
+            </th>
+            <th class='text-left nowrap' width="10%">商品信息</th>
+            <th class='text-left nowrap' width="10%">商品状态</th>
+            <th class='text-left nowrap padding-0 relative' style="min-width:400px">
+                <div class="layui-row" style="line-height:28px">
+                    <div class="layui-col-xs6 text-center layui-elip">商品规格</div>
+                    <div class="layui-col-xs3 text-center layui-elip">市价 / 售价</div>
+                    <div class="layui-col-xs3 text-center layui-elip">库存 / 销量</div>
                 </div>
-                {/foreach}
-            </div>
-        </td>
-        <td class='text-left nowrap'>
-
-            <div class="nowrap margin-bottom-5">
-                {if auth("store/goods/edit")}
-                <a data-dbclick class="layui-btn layui-btn-sm" data-open='{:url("edit")}?id={$vo.id}'>编 辑</a>
-                {else}
-                <a data-tips-text="您没有编辑商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">编 辑</a>
-                {/if}
+            </th>
+            <th></th>
+        </tr>
+        </thead>
+        {/notempty}
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr data-dbclick>
+            <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
+            <td class='list-table-sort-td'><input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input"></td>
+            <td class='text-left nowrap'>
+                {notempty name='vo.logo'}
+                <img data-tips-image style="width:60px;height:60px" src="{$vo.logo|default=''}" class="margin-right-5 text-top">
+                {/notempty}
+                <div class="inline-block">
+                    商品编号:{$vo.id|default='--'}<br>
+                    所属分类:{$vo.cate.title|default='--'}<br>
+                    商品名称:{$vo.title|default='--'}<br>
+                </div>
+            </td>
+            <td class='text-left nowrap'>
+                快递费用:{$vo.price_express|default='0.00'} 元<br>
+                分成比例:{$vo.price_rate+0} %<br>
+                销售状态:{eq name='vo.status' value='0'}<span class="layui-badge">已下架</span>{else}<span class="layui-badge layui-bg-green">销售中</span>{/eq}<br>
+            </td>
+            <td class='text-left nowrap padding-0 relative'>
+                <div style="overflow:auto;max-height:68px;padding:5px 0">
+                    {foreach $vo.list as $goods}
+                    <div class="layui-row" style="line-height:23px">
+                        <div class="layui-col-xs6 text-center layui-elip font-s10">{:str_replace(['::',';;'],[':',';'],$goods.goods_spec)}</div>
+                        <div class="layui-col-xs3 text-center layui-elip color-blue font-s10">{$goods.price_market+0} / {$goods.price_selling+0}</div>
+                        <div class="layui-col-xs3 text-center layui-elip color-blue font-s10">{$goods.number_stock+0} / {$goods.number_sales+0}</div>
+                    </div>
+                    {/foreach}
+                </div>
+            </td>
+            <td class='text-left nowrap'>
 
-                {if isset($vo.status) and $vo.status eq 1}
-                <!--{if auth("store/goods/forbid")}-->
-                <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0">下 架</a>
-                <!--{else}-->
-                <a data-tips-text="您没有下架商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">下 架</a>
-                <!--{/if}-->
-                {else}
-                <!--{if auth("store/goods/resume")}-->
-                <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1">上 架</a>
-                <!--{else}-->
-                <a data-tips-text="您没有上架商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">上 架</a>
-                <!--{/if}-->
-                {/if}
-            </div>
+                <div class="nowrap margin-bottom-5">
+                    {if auth("store/goods/edit")}
+                    <a data-dbclick class="layui-btn layui-btn-sm" data-open='{:url("edit")}?id={$vo.id}'>编 辑</a>
+                    {else}
+                    <a data-tips-text="您没有编辑商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">编 辑</a>
+                    {/if}
 
-            <div class="nowrap margin-bottom-5">
+                    {if isset($vo.status) and $vo.status eq 1}
+                    <!--{if auth("store/goods/forbid")}-->
+                    <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0">下 架</a>
+                    <!--{else}-->
+                    <a data-tips-text="您没有下架商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">下 架</a>
+                    <!--{/if}-->
+                    {else}
+                    <!--{if auth("store/goods/resume")}-->
+                    <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1">上 架</a>
+                    <!--{else}-->
+                    <a data-tips-text="您没有上架商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">上 架</a>
+                    <!--{/if}-->
+                    {/if}
+                </div>
 
-                {if auth("store/goods/stock")}
-                <a class="layui-btn layui-btn-sm layui-btn-normal" data-title="商品入库" data-modal='{:url("stock")}?id={$vo.id}'>入 库</a>
-                {else}
-                <a data-tips-text="您没有商品入库的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">入 库</a>
-                {/if}
+                <div class="nowrap margin-bottom-5">
 
-                {if auth("store/goods/del")}
-                <a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除数据吗?" data-action="{:url('del')}" data-value="id#{$vo.id}">删 除</a>
-                {else}
-                <a data-tips-text="您没有删除商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">删 除</a>
-                {/if}
+                    {if auth("store/goods/stock")}
+                    <a class="layui-btn layui-btn-sm layui-btn-normal" data-title="商品入库" data-modal='{:url("stock")}?id={$vo.id}'>入 库</a>
+                    {else}
+                    <a data-tips-text="您没有商品入库的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">入 库</a>
+                    {/if}
 
-            </div>
+                    {if auth("store/goods/remove")}
+                    <a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除数据吗?" data-action="{:url('remove')}" data-value="id#{$vo.id}">删 除</a>
+                    {else}
+                    <a data-tips-text="您没有删除商品的权限哦!" class="layui-btn layui-btn-sm layui-btn-primary layui-disabled">删 除</a>
+                    {/if}
 
-        </td>
-    </tr>
-    <!--{/foreach}-->
-    </tbody>
-</table>
+                </div>
 
-{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+            </td>
+        </tr>
+        {/foreach}
+        </tbody>
+    </table>
 
-<style>
-    table.trim-bottom-border tr:last-child td {
-        border: none !important;
-    }
-</style>
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
 
+</div>
 {/block}

+ 2 - 2
application/store/view/goods/index_search.html

@@ -11,7 +11,7 @@
             <label class="layui-form-label">商品分类</label>
             <div class="layui-input-inline">
                 <select class="layui-select" name="cate_id" lay-search>
-                    <option value="">-- 所有商品 --</option>
+                    <option value="">-- 全部商品 --</option>
                     {foreach $clist as $v}
                     <!--{eq name='Think.get.cate_id' value='$v.id.""'}-->
                     <option selected value="{$v.id}">{$v.title}</option>
@@ -26,7 +26,7 @@
             <label class="layui-form-label">销售状态</label>
             <div class="layui-input-inline">
                 <select class="layui-select" name="status">
-                    {foreach [''=>'- 全部商品 -','1'=>'销售中商品','0'=>'已下架商品'] as $k=>$v}
+                    {foreach [''=>'- 全部状态 -','1'=>'销售中商品','0'=>'已下架商品'] as $k=>$v}
                     <!--{eq name='Think.get.status' value='$k.""'}-->
                     <option selected value="{$k}">{$v}</option>
                     <!--{else}-->

+ 15 - 13
application/store/view/goods_cate/form.html

@@ -2,26 +2,28 @@
 
     <div class="layui-card-body">
 
-        <div class="layui-form-item">
-            <label class="layui-form-label">商品分类图片</label>
-            <div class="layui-input-block">
+        <div class="layui-row margin-bottom-15">
+            <label class="layui-col-xs2 think-form-label">商品分类图片</label>
+            <label class="layui-col-xs8 think-form-group-left">
                 <input name="logo" required value='{$vo.logo|default=""}' placeholder="请上传分类图片" class="layui-input">
-                <button data-file="btn" data-field="logo" data-type="png,jpg,gif" class="layui-btn layui-btn-sm" type="button">上传图片</button>
-            </div>
+            </label>
+            <a class="layui-col-xs2 layui-btn think-form-group-right" data-file="btn" data-field="logo" data-type="png,jpg,gif">
+                <i class="layui-icon layui-icon-upload"></i> 上传图片
+            </a>
         </div>
 
-        <div class="layui-form-item">
-            <label class="layui-form-label">商品分类名称</label>
-            <div class="layui-input-block">
+        <div class="layui-row margin-bottom-15">
+            <label class="layui-col-xs2 think-form-label">商品分类名称</label>
+            <label class="layui-col-xs10">
                 <input name="title" required value='{$vo.title|default=""}' placeholder="请输入商品分类名称" class="layui-input">
-            </div>
+            </label>
         </div>
 
-        <div class="layui-form-item">
-            <label class="layui-form-label">商品分类描述</label>
-            <div class="layui-input-block">
+        <div class="layui-row margin-bottom-15">
+            <label class="layui-col-xs2 think-form-label">商品分类描述</label>
+            <label class="layui-col-xs10">
                 <textarea class="layui-textarea" name="desc">{$vo.desc|default=''}</textarea>
-            </div>
+            </label>
         </div>
 
     </div>

+ 60 - 60
application/store/view/goods_cate/index.html

@@ -1,72 +1,72 @@
 {extend name='admin@main'}
 
 {block name="button"}
-<!--{if auth("store/goods_cate/add")}-->
+{if auth("store/goods_cate/add")}
 <button data-modal='{:url("add")}' data-title="添加商品分类" class='layui-btn layui-btn-sm layui-btn-primary'>添加商品分类</button>
-<!--{/if}-->
-<!--{if auth("store/goods_cate/del")}-->
-<button data-action='{:url("del")}' data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除商品分类</button>
-<!--{/if}-->
+{/if}
+{if auth("store/goods_cate/remove")}
+<button data-action='{:url("remove")}' data-rule="id#{key}" class='layui-btn layui-btn-sm layui-btn-primary'>删除商品分类</button>
+{/if}
 {/block}
 
 {block name="content"}
-<table class="layui-table" lay-skin="line">
-    <caption class="margin-bottom-10 text-left">{include file='goods_cate/index_search'}</caption>
-    <!--{notempty name='list'}-->
-    <thead>
-    <tr>
-        <th class='list-table-check-td think-checkbox'>
-            <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
-        </th>
-        <th class='list-table-sort-td'>
-            <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
-        </th>
-        <th class='text-left nowrap'>分类名称</th>
-        <th class="text-center">状态</th>
-        <th class="text-center"></th>
-        <th></th>
-    </tr>
-    </thead>
-    <!--{/notempty}-->
-    <tbody>
-    <!--{foreach $list as $key=>$vo}-->
-    <tr>
-        <td class='list-table-check-td think-checkbox'>
-            <input class="list-check-box" value='{$vo.id}' type='checkbox'>
-        </td>
-        <td class='list-table-sort-td'>
-            <input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input">
-        </td>
-        <td class='text-left nowrap'>
-            <a data-tips-image="{$vo.logo|default=''}" class="fa fa-image font-s14 margin-right-5"></a>
-            {$vo.title|default=''}
-        </td>
-        <td class='text-center nowrap'>
-            {eq name='vo.status' value='0'}<span class="layui-badge">已禁用</span>{else}<span class="layui-badge layui-bg-green">使用中</span>{/eq}<br>
-        </td>
-        <td class='text-center nowrap'>{$vo.create_at|format_datetime}</td>
-        <td class='text-left nowrap'>
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='goods_cate/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class='list-table-sort-td'>
+                <button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
+            </th>
+            <th class='text-left nowrap'>分类名称</th>
+            <th class="text-center">状态</th>
+            <th class="text-center"></th>
+            <th></th>
+        </tr>
+        </thead>
+        {/notempty}
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr>
+            <td class='list-table-check-td think-checkbox'>
+                <input class="list-check-box" value='{$vo.id}' type='checkbox'>
+            </td>
+            <td class='list-table-sort-td'>
+                <input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input">
+            </td>
+            <td class='text-left nowrap'>
+                <a data-tips-image="{$vo.logo|default=''}" class="fa fa-image font-s14 margin-right-5"></a>
+                {$vo.title|default=''}
+            </td>
+            <td class='text-center nowrap'>
+                {eq name='vo.status' value='0'}<span class="layui-badge">已禁用</span>{else}<span class="layui-badge layui-bg-green">使用中</span>{/eq}<br>
+            </td>
+            <td class='text-center nowrap'>{$vo.create_at|format_datetime}</td>
+            <td class='text-left nowrap'>
 
-            {if auth("store/goods_cate/edit")}
-            <a data-title="编辑商品分类" class="layui-btn layui-btn-sm" data-modal='{:url("edit")}?id={$vo.id}'>编 辑</a>
-            {/if}
+                {if auth("store/goods_cate/edit")}
+                <a data-title="编辑商品分类" class="layui-btn layui-btn-sm" data-modal='{:url("edit")}?id={$vo.id}'>编 辑</a>
+                {/if}
 
-            {if $vo.status eq 1 and auth("store/goods_cate/forbid")}
-            <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0">禁 用</a>
-            {elseif auth("store/goods_cate/resume")}
-            <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1">启 用</a>
-            {/if}
+                {if $vo.status eq 1 and auth("store/goods_cate/forbid")}
+                <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('forbid')}" data-value="id#{$vo.id};status#0">禁 用</a>
+                {elseif auth("store/goods_cate/resume")}
+                <a class="layui-btn layui-btn-sm layui-btn-warm" data-action="{:url('resume')}" data-value="id#{$vo.id};status#1">启 用</a>
+                {/if}
 
-            {if auth("store/goods_cate/del")}
-            <a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除数据吗?" data-action="{:url('del')}" data-value="id#{$vo.id}">删 除</a>
-            {/if}
-
-        </td>
-    </tr>
-    <!--{/foreach}-->
-    </tbody>
-</table>
-
-{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+                {if auth("store/goods_cate/remove")}
+                <a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除数据吗?" data-action="{:url('remove')}" data-value="id#{$vo.id}">删 除</a>
+                {/if}
 
+            </td>
+        </tr>
+        {/foreach}
+        </tbody>
+    </table>
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+</div>
 {/block}

+ 1 - 1
application/store/view/goods_cate/index_search.html

@@ -11,7 +11,7 @@
             <label class="layui-form-label">使用状态</label>
             <div class="layui-input-inline">
                 <select class="layui-select" name="status">
-                    {foreach [''=>'- 全部商品分类 -','1'=>'使用中的商品分类','0'=>'已禁用的商品分类'] as $k=>$v}
+                    {foreach [''=>'- 全部状态 -','1'=>'使用中的商品分类','0'=>'已禁用的商品分类'] as $k=>$v}
                     <!--{eq name='Think.get.status' value='$k.""'}-->
                     <option selected value="{$k}">{$v}</option>
                     <!--{else}-->

+ 41 - 41
application/store/view/member/index.html

@@ -1,45 +1,45 @@
 {extend name='admin@main'}
 
 {block name="content"}
-<table class="layui-table" lay-skin="line">
-    <caption class="margin-bottom-10 text-left">{include file='member/index_search'}</caption>
-    <!--{notempty name='list'}-->
-    <thead>
-    <tr>
-        <th class='list-table-check-td think-checkbox'>
-            <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
-        </th>
-        <th class='text-left nowrap'>会员昵称</th>
-        <th class='text-left nowrap'>会员手机</th>
-        <th class='text-left nowrap'>会员级别</th>
-        <th class='text-left nowrap'>注册时间</th>
-    </tr>
-    </thead>
-    <!--{/notempty}-->
-    <tbody>
-    <!--{foreach $list as $key=>$vo}-->
-    <tr>
-        <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
-        <td class='text-left nowrap'>
-            {notempty name='vo.headimg'}
-            <img data-tips-image style="width:20px;height:20px;vertical-align:top" src="{$vo.headimg|default=''}" class="margin-right-5">
-            {/notempty}
-            <div class="inline-block">{$vo.nickname|default='--'}</div>
-        </td>
-        <td class='text-left'>{$vo.phone|default='--'}</td>
-        <td class='text-left'>
-            {if $vo.vip_level eq 0} 游客会员
-            {elseif $vo.vip_level eq 1} 临时会员({$vo.vip_date|default=''})
-            {elseif $vo.vip_level eq 2} VIP1会员({$vo.vip_date|default=''})
-            {elseif $vo.vip_level eq 3} VIP2会员({$vo.vip_date|default=''})
-            {/if}
-        </td>
-        <td class='text-left'>{$vo.create_at|format_datetime}</td>
-    </tr>
-    <!--{/foreach}-->
-    </tbody>
-</table>
-
-{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
-
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='member/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class='text-left nowrap'>会员昵称</th>
+            <th class='text-left nowrap'>会员手机</th>
+            <th class='text-left nowrap'>会员级别</th>
+            <th class='text-left nowrap'>注册时间</th>
+        </tr>
+        </thead>
+        {/notempty}
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr>
+            <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
+            <td class='text-left nowrap'>
+                {notempty name='vo.headimg'}
+                <img data-tips-image style="width:20px;height:20px;vertical-align:top" src="{$vo.headimg|default=''}" class="margin-right-5">
+                {/notempty}
+                <div class="inline-block">{$vo.nickname|default='--'}</div>
+            </td>
+            <td class='text-left'>{$vo.phone|default='--'}</td>
+            <td class='text-left'>
+                {if $vo.vip_level eq 0} 游客会员
+                {elseif $vo.vip_level eq 1} 临时会员({$vo.vip_date|default=''})
+                {elseif $vo.vip_level eq 2} VIP1会员({$vo.vip_date|default=''})
+                {elseif $vo.vip_level eq 3} VIP2会员({$vo.vip_date|default=''})
+                {/if}
+            </td>
+            <td class='text-left'>{$vo.create_at|format_datetime}</td>
+        </tr>
+        {/foreach}
+        </tbody>
+    </table>
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+</div>
 {/block}

+ 36 - 30
application/store/view/message/index.html

@@ -1,34 +1,40 @@
 {extend name='admin@main'}
 
 {block name="content"}
-<table class="layui-table" lay-skin="line">
-    <caption class="margin-bottom-10 text-left">{include file='message/index_search'}</caption>
-    <!--{notempty name='list'}-->
-    <thead>
-    <tr>
-        <th class='list-table-check-td think-checkbox'>
-            <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
-        </th>
-        <th class='text-left nowrap'>发送手机</th>
-        <th class='text-left nowrap'>短信内容</th>
-        <th class='text-left nowrap'>返回结果</th>
-        <th class='text-left nowrap'>发送时间</th>
-    </tr>
-    </thead>
-    <!--{/notempty}-->
-    <tbody>
-    <!--{foreach $list as $key=>$vo}-->
-    <tr>
-        <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
-        <td>{$vo.phone|default=''}</td>
-        <td>{$vo.content|default=''}</td>
-        <td>{$vo.result|default=''}</td>
-        <td>{$vo.create_at|default=''}<br></td>
-    </tr>
-    <!--{/foreach}-->
-    </tbody>
-</table>
-
-{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
-
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='message/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class='text-left nowrap'>发送手机</th>
+            <th class='text-left nowrap'>短信内容</th>
+            <th class='text-left nowrap'>返回结果</th>
+            <th class='text-left nowrap'>发送时间</th>
+            <th></th>
+        </tr>
+        </thead>
+        {/notempty}
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr>
+            <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
+            <td>{$vo.phone|default=''}</td>
+            <td>{$vo.content|default=''}</td>
+            <td>{$vo.result|default=''}</td>
+            <td>{$vo.create_at|default=''}<br></td>
+            <td>
+                {if auth("store/member/remove")}
+                <a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要删除数据吗?" data-action="{:url('remove')}" data-value="id#{$vo.id}">删 除</a>
+                {/if}
+            </td>
+        </tr>
+        {/foreach}
+        </tbody>
+    </table>
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+</div>
 {/block}

+ 80 - 83
application/store/view/order/index.html

@@ -1,89 +1,86 @@
 {extend name='admin@main'}
 
 {block name="content"}
-<table class="layui-table" lay-skin="line">
-    <caption class="margin-bottom-10 text-left">{include file='order/index_search'}</caption>
-    <!--{notempty name='list'}-->
-    <thead>
-    <tr>
-        <th class='list-table-check-td think-checkbox'>
-            <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
-        </th>
-        <th class="text-left nowrap">会员信息</th>
-        <th class='text-left nowrap'>订单信息</th>
-        <th class='text-left nowrap'>发货信息</th>
-        <th class='text-right nowrap'>商品信息</th>
-    </tr>
-    </thead>
-    <!--{/notempty}-->
-    <tbody>
-    <!--{foreach $list as $key=>$vo}-->
-    <tr>
-        <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
-        <td class="text-left nowrap relative">
-            {notempty name='vo.member.headimg'}
-            <img data-tips-image style="width:80px;height:80px" src="{$vo.member.headimg|default=''}" class="margin-right-5 text-top">
-            {/notempty}
-            <div class="inline-block">
-                推荐会员:{$vo.from_member.nickname|default='--'}<br>
-                推荐手机:{$vo.from_member.phone|default='--'}<br>
-                会员昵称:{$vo.member.nickname|default='--'}<br>
-                会员手机:{$vo.member.phone|default='--'}<br>
-            </div>
-        </td>
-        <td class='text-left nowrap'>
-            订单单号:<span class="color-blue">{$vo.order_no|default=''}</span>
-            {eq name='vo.status' value='1'}<span class="layui-badge layui-bg-red margin-left-5">预订单</span>{/eq}
-            {eq name='vo.status' value='2'}<span class="layui-badge layui-bg-orange margin-left-5">待付款</span>{/eq}
-            {eq name='vo.status' value='3'}<span class="layui-badge layui-bg-black margin-left-5">待发货</span>{/eq}
-            {eq name='vo.status' value='4'}<span class="layui-badge layui-bg-green margin-left-5">已发货</span>{/eq}
-            {eq name='vo.status' value='5'}<span class="layui-badge layui-bg-blue margin-left-5">已完成</span>{/eq}
-            <br>
-            订单金额:<strong class="color-blue">{$vo.price_total+0}</strong> 元
-            分成 <strong class="color-blue">{$vo.price_rate_amount+0}</strong> 元
-            已支付 <strong class="color-blue">{$vo.pay_price+0}</strong> 元<br>
-            下单时间:{$vo.create_at|format_datetime}<br>
-            支付时间:{$vo.pay_at|format_datetime|default='--'}<br>
-        </td>
-        <td class="text-left nowrap">
-            收货信息:{$vo.express_name|default='--'}<span class="margin-left-5 color-blue">{$vo.express_phone}</span><br>
-            收货地址:{$vo.express_province|default='--'}{$vo.express_city}{$vo.express_area}{$vo.express_address}<br>
-            发货状态:{eq name='vo.express_state' value='0'}
-            <span class="layui-badge layui-bg-black">未发货</span>
-            {else}
-            <span class="layui-badge layui-bg-blue">{$vo.express_company_title|default='--'}</span>
-            <a data-title="{$vo.express_company_title}({$vo.express_send_no})" data-tips-text="快递追踪查询"
-               data-modal="{:url('expressQuery')}?code={$vo.express_company_code}&no={$vo.express_send_no}"
-               class="layui-badge layui-bg-gray margin-left-5">{$vo.express_send_no|default='--'}</a>
-            {/eq}
-            {if $vo.status eq 3}
-            <a class=" margin-left-5" data-title="填写订单信息" data-modal="{:url('express')}?id={$vo.id}">填写发货信息</a>
-            {elseif $vo.status eq 4}
-            <a class=" margin-left-5" data-title="修改发货信息" data-modal="{:url('express')}?id={$vo.id}">修改发货信息</a>
-            {/if}
-            <br>
-            发货时间:{$vo.express_send_at|format_datetime}<br>
-        </td>
-        <td class="nowrap">
-            {foreach $vo.list as $g}
-            <div class="nowrap">
-                <p class="text-right">{$g.goods_title|default=''} x{$g.number_goods|default=0}</p>
-                <p class="text-right color-desc">
-                    售价 {$g.price_real} 元,分成比例 {$g.price_rate+0}%,分成金额 {$g.price_rate_amount+0} 元
-                </p>
-            </div>
-            {/foreach}
-        </td>
-    </tr>
-    <!--{/foreach}-->
-    </tbody>
-</table>
+<div class="think-box-shadow">
+    <table class="layui-table" lay-skin="line">
+        <caption class="margin-bottom-10 text-left">{include file='order/index_search'}</caption>
+        {notempty name='list'}
+        <thead>
+        <tr>
+            <th class='list-table-check-td think-checkbox'>
+                <input data-auto-none data-check-target='.list-check-box' type='checkbox'>
+            </th>
+            <th class="text-left nowrap">会员信息</th>
+            <th class='text-left nowrap'>订单信息</th>
+            <th class='text-left nowrap'>发货信息</th>
+            <th class='text-right nowrap'>商品信息</th>
+        </tr>
+        </thead>
+        {/notempty}
+        <tbody>
+        {foreach $list as $key=>$vo}
+        <tr>
+            <td class='list-table-check-td think-checkbox'><input class="list-check-box" value='{$vo.id}' type='checkbox'></td>
+            <td class="text-left nowrap relative">
+                {notempty name='vo.member.headimg'}
+                <img data-tips-image style="width:80px;height:80px" src="{$vo.member.headimg|default=''}" class="margin-right-5 text-top">
+                {/notempty}
+                <div class="inline-block">
+                    推荐会员:{$vo.from_member.nickname|default='--'}<br>
+                    推荐手机:{$vo.from_member.phone|default='--'}<br>
+                    会员昵称:{$vo.member.nickname|default='--'}<br>
+                    会员手机:{$vo.member.phone|default='--'}<br>
+                </div>
+            </td>
+            <td class='text-left nowrap'>
+                订单单号:<span class="color-blue">{$vo.order_no|default=''}</span>
+                {eq name='vo.status' value='1'}<span class="layui-badge layui-bg-red margin-left-5">预订单</span>{/eq}
+                {eq name='vo.status' value='2'}<span class="layui-badge layui-bg-orange margin-left-5">待付款</span>{/eq}
+                {eq name='vo.status' value='3'}<span class="layui-badge layui-bg-black margin-left-5">待发货</span>{/eq}
+                {eq name='vo.status' value='4'}<span class="layui-badge layui-bg-green margin-left-5">已发货</span>{/eq}
+                {eq name='vo.status' value='5'}<span class="layui-badge layui-bg-blue margin-left-5">已完成</span>{/eq}
+                <br>
+                订单金额:<strong class="color-blue">{$vo.price_total+0}</strong> 元
+                分成 <strong class="color-blue">{$vo.price_rate_amount+0}</strong> 元
+                已支付 <strong class="color-blue">{$vo.pay_price+0}</strong> 元<br>
+                下单时间:{$vo.create_at|format_datetime}<br>
+                支付时间:{$vo.pay_at|format_datetime|default='--'}<br>
+            </td>
+            <td class="text-left nowrap">
+                收货信息:{$vo.express_name|default='--'}<span class="margin-left-5 color-blue">{$vo.express_phone}</span><br>
+                收货地址:{$vo.express_province|default='--'}{$vo.express_city}{$vo.express_area}{$vo.express_address}<br>
+                发货状态:{eq name='vo.express_state' value='0'}
+                <span class="layui-badge layui-bg-black">未发货</span>
+                {else}
+                <span class="layui-badge layui-bg-blue">{$vo.express_company_title|default='--'}</span>
+                <a data-title="{$vo.express_company_title}({$vo.express_send_no})" data-tips-text="快递追踪查询"
+                   data-modal="{:url('expressQuery')}?code={$vo.express_company_code}&no={$vo.express_send_no}"
+                   class="layui-badge layui-bg-gray margin-left-5">{$vo.express_send_no|default='--'}</a>
+                {/eq}
+                {if $vo.status eq 3}
+                <a class=" margin-left-5" data-title="填写订单信息" data-modal="{:url('express')}?id={$vo.id}">填写发货信息</a>
+                {elseif $vo.status eq 4}
+                <a class=" margin-left-5" data-title="修改发货信息" data-modal="{:url('express')}?id={$vo.id}">修改发货信息</a>
+                {/if}
+                <br>
+                发货时间:{$vo.express_send_at|format_datetime}<br>
+            </td>
+            <td class="nowrap">
+                {foreach $vo.list as $g}
+                <div class="nowrap">
+                    <p class="text-right">{$g.goods_title|default=''} x{$g.number_goods|default=0}</p>
+                    <p class="text-right color-desc">
+                        售价 {$g.price_real} 元,分成比例 {$g.price_rate+0}%,分成金额 {$g.price_rate_amount+0} 元
+                    </p>
+                </div>
+                {/foreach}
+            </td>
+        </tr>
+        {/foreach}
+        </tbody>
+    </table>
 
-{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
+    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
 
-<style>
-    table.trim-bottom-border tr:last-child td {
-        border: none !important
-    }
-</style>
+</div>
 {/block}

+ 0 - 1
application/store/view/order/index_search.html

@@ -91,7 +91,6 @@
         </div>
         <div class="layui-form-item layui-inline">
             <button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
-            <button type="button" data-export-list class="layui-btn layui-btn-primary"><i class="layui-icon layui-icon-export"></i> 导 出</button>
         </div>
     </form>
     <script>

+ 75 - 0
application/wechat/queue/WechatQueue.php

@@ -0,0 +1,75 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\wechat\queue;
+
+use app\admin\queue\JobsQueue;
+use app\wechat\service\FansService;
+use app\wechat\service\WechatService;
+use think\Db;
+
+/**
+ * Class Jobs
+ * @package app\wechat
+ */
+class WechatQueue extends JobsQueue
+{
+    /**
+     * 当前任务URI
+     */
+    const URI = self::class;
+
+    /**
+     * 执行任务
+     * @return boolean
+     */
+    public function execute()
+    {
+        try {
+            $appid = WechatService::getAppid();
+            $wechat = WechatService::WeChatUser();
+            $next = ''; // 获取远程粉丝
+            $this->output->writeln('Start synchronizing fans from the Wechat server');
+            while (is_array($result = $wechat->getUserList($next)) && !empty($result['data']['openid'])) {
+                foreach (array_chunk($result['data']['openid'], 100) as $chunk)
+                    if (is_array($list = $wechat->getBatchUserInfo($chunk)) && !empty($list['user_info_list']))
+                        foreach ($list['user_info_list'] as $user) FansService::set($user, $appid);
+                if (in_array($result['next_openid'], $result['data']['openid'])) break;
+                $next = $result['next_openid'];
+            }
+            $next = ''; // 同步粉丝黑名单
+            $this->output->writeln('Start synchronizing black from the Wechat server');
+            while (is_array($result = $wechat->getBlackList($next)) && !empty($result['data']['openid'])) {
+                foreach (array_chunk($result['data']['openid'], 100) as $chunk) {
+                    $where = [['is_black', 'eq', '0'], ['openid', 'in', $chunk]];
+                    Db::name('WechatFans')->where($where)->update(['is_black' => '1']);
+                }
+                if (in_array($result['next_openid'], $result['data']['openid'])) break;
+                $next = $result['next_openid'];
+            }
+            // 同步粉丝标签列表
+            $this->output->writeln('Start synchronizing tags from the Wechat server');
+            if (is_array($list = WechatService::WeChatTags()->getTags()) && !empty($list['tags'])) {
+                foreach ($list['tags'] as &$tag) $tag['appid'] = $appid;
+                Db::name('WechatFansTags')->where('1=1')->delete();
+                Db::name('WechatFansTags')->insertAll($list['tags']);
+            }
+            return true;
+        } catch (\Exception $e) {
+            $this->statusDesc = $e->getMessage();
+            return false;
+        }
+    }
+
+}

+ 68 - 0
application/wechat/service/FansService.php

@@ -0,0 +1,68 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\wechat\service;
+
+use think\Db;
+
+/**
+ * 微信粉丝信息
+ * Class FansService
+ * @package app\wechat\service
+ */
+class FansService
+{
+
+    /**
+     * 增加或更新粉丝信息
+     * @param array $user 粉丝信息
+     * @param string $appid 公众号APPID
+     * @return boolean
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function set(array $user, $appid = '')
+    {
+        if (!empty($user['subscribe_time'])) {
+            $user['subscribe_at'] = date('Y-m-d H:i:s', $user['subscribe_time']);
+        }
+        if (isset($user['tagid_list']) && is_array($user['tagid_list'])) {
+            $user['tagid_list'] = is_array($user['tagid_list']) ? join(',', $user['tagid_list']) : '';
+        }
+        foreach (['country', 'province', 'city', 'nickname', 'remark'] as $k) {
+            isset($user[$k]) && $user[$k] = emoji_encode($user[$k]);
+        }
+        if ($appid !== '') $user['appid'] = $appid;
+        unset($user['privilege'], $user['groupid']);
+        return data_save('WechatFans', $user, 'openid');
+    }
+
+    /**
+     * 获取粉丝信息
+     * @param string $openid
+     * @return array|null
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function get($openid)
+    {
+        $user = Db::name('WechatFans')->where(['openid' => $openid])->find();
+        foreach (['country', 'province', 'city', 'nickname', 'remark'] as $k) {
+            isset($user[$k]) && $user[$k] = emoji_decode($user[$k]);
+        }
+        return $user;
+    }
+
+}

+ 83 - 0
application/wechat/service/MediaService.php

@@ -0,0 +1,83 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\wechat\service;
+
+use library\File;
+use think\Db;
+use WeChat\Contracts\MyCurlFile;
+
+/**
+ * 微信素材管理
+ * Class MediaService
+ * @package app\wechat\service
+ */
+class MediaService
+{
+    /**
+     * 通过图文ID读取图文信息
+     * @param integer $id 本地图文ID
+     * @param array $where 额外的查询条件
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function news($id, $where = [])
+    {
+        $data = Db::name('WechatNews')->where(['id' => $id])->where($where)->find();
+        list($data['articles'], $articleIds) = [[], explode(',', $data['article_id'])];
+        $articles = Db::name('WechatNewsArticle')->whereIn('id', $articleIds)->select();
+        foreach ($articleIds as $article_id) foreach ($articles as $article) {
+            if (intval($article['id']) === intval($article_id)) array_push($data['articles'], $article);
+            unset($article['create_by'], $article['create_at']);
+        }
+        return $data;
+    }
+
+    /**
+     * 上传图片永久素材,返回素材media_id
+     * @param string $url 文件URL地址
+     * @param string $type 文件类型
+     * @param array $videoInfo 视频信息
+     * @return string|null
+     * @throws \WeChat\Exceptions\InvalidResponseException
+     * @throws \WeChat\Exceptions\LocalCacheException
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function upload($url, $type = 'image', $videoInfo = [])
+    {
+        $where = ['md5' => md5($url), 'appid' => WechatService::getAppid()];
+        if (($mediaId = Db::name('WechatMedia')->where($where)->value('media_id'))) return $mediaId;
+        $result = WechatService::WeChatMedia()->addMaterial(self::getServerPath($url), $type, $videoInfo);
+        data_save('WechatMedia', [
+            'local_url' => $url, 'md5' => $where['md5'], 'appid' => WechatService::getAppid(), 'type' => $type,
+            'media_url' => isset($result['url']) ? $result['url'] : '', 'media_id' => $result['media_id'],
+        ], 'type', $where);
+        return $result['media_id'];
+    }
+
+    /**
+     * 文件位置处理
+     * @param string $local
+     * @return string
+     * @throws \WeChat\Exceptions\LocalCacheException
+     */
+    private static function getServerPath($local)
+    {
+        if (file_exists($local)) return new MyCurlFile($local);
+        return new MyCurlFile(File::down($local)['file']);
+    }
+}

+ 248 - 0
application/wechat/service/WechatService.php

@@ -0,0 +1,248 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\wechat\service;
+
+/**
+ * 微信处理管理
+ * Class WechatService
+ * @package app\wechat\service
+ *
+ * ----- WeOpen for Open -----
+ * @method \WeOpen\Login login() static 第三方微信登录
+ * @method \WeOpen\Service service() static 第三方服务
+ *
+ * ----- WeMini for Open -----
+ * @method \WeMini\Code WeMiniCode() static 小程序代码管理
+ * @method \WeMini\User WeMiniUser() static 小程序帐号管理
+ * @method \WeMini\Basic WeMiniBasic() static 小程序基础信息
+ * @method \WeMini\Domain WeMiniDomain() static 小程序域名管理
+ * @method \WeMini\Tester WeMiniTester() static 小程序成员管理
+ * @method \WeMini\Account WeMiniAccount() static 小程序账号管理
+ *
+ * ----- ThinkService -----
+ * @method mixed wechat() static 第三方微信工具
+ */
+class WechatService extends \We
+{
+
+    /**
+     * 接口类型模式
+     * @var string
+     */
+    private static $type = 'WeChat';
+
+    /**
+     * 获取微信支付配置
+     * @param array|null $options
+     * @return array
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function config($options = null)
+    {
+        if (empty($options)) $options = [
+            // 微信功能必需参数
+            'appid'          => self::getAppid(),
+            'token'          => sysconf('wechat_token'),
+            'appsecret'      => sysconf('wechat_appsecret'),
+            'encodingaeskey' => sysconf('wechat_encodingaeskey'),
+            // 微信支付必要参数
+            'mch_id'         => sysconf('wechat_mch_id'),
+            'mch_key'        => sysconf('wechat_mch_key'),
+            'cache_path'     => env('runtime_path') . 'wechat' . DIRECTORY_SEPARATOR,
+        ];
+        if (sysconf('wechat_mch_ssl_type') === 'p12') {
+            $options['ssl_p12'] = self::_parseCertPath(sysconf('wechat_mch_ssl_p12'));
+        } else {
+            $options['ssl_key'] = self::_parseCertPath(sysconf('wechat_mch_ssl_key'));
+            $options['ssl_cer'] = self::_parseCertPath(sysconf('wechat_mch_ssl_cer'));
+        }
+        return parent::config($options);
+    }
+
+    /**
+     * 解析证书路径
+     * @param string $path
+     * @return mixed
+     * @throws \think\Exception
+     */
+    private static function _parseCertPath($path)
+    {
+        if (preg_match('|^[a-z0-9]{16,16}\/[a-z0-9]{16,16}\.|i', $path)) {
+            return \library\File::instance('local')->path($path, true);
+        }
+        return $path;
+    }
+
+    /**
+     * 静态魔术加载方法
+     * @param string $name 静态类名
+     * @param array $arguments 参数集合
+     * @return mixed
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function __callStatic($name, $arguments)
+    {
+        $config = [];
+        if (is_array($arguments) && count($arguments) > 0) {
+            $option = array_shift($arguments);
+            $config = is_array($option) ? $option : self::config();
+        }
+        if (in_array($name, ['wechat'])) {
+            return self::instance(trim($name, '_'), 'WeChat', $config);
+        } elseif (substr($name, 0, 6) === 'WeChat') {
+            return self::instance(substr($name, 6), 'WeChat', $config);
+        } elseif (substr($name, 0, 6) === 'WeMini') {
+            return self::instance(substr($name, 6), 'WeMini', $config);
+        } elseif (substr($name, 0, 5) === 'WePay') {
+            return self::instance(substr($name, 5), 'WePay', $config);
+        } elseif (substr($name, 0, 6) === 'AliPay') {
+            return self::instance(substr($name, 6), 'AliPay', $config);
+        }
+        throw new \think\Exception("class {$name} not found");
+    }
+
+    /**
+     * 接口对象实例化
+     * @param string $name 接口名称
+     * @param string $type 接口类型
+     * @param array $config 微信配置
+     * @return mixed
+     * @throws \SoapFault
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function instance($name, $type = 'WeChat', $config = [])
+    {
+        if (!in_array($type, ['WeChat', 'WeMini', 'WePay', 'AliPay'])) $type = self::$type;
+        if (self::getType() === 'api' || in_array($type, ['WePay', 'AliPay'])) {
+            $class = "\\{$type}\\" . ucfirst(strtolower($name));
+            if (class_exists($class)) return new $class(empty($config) ? self::config() : $config);
+            throw new \think\Exception("Class '{$class}' not found");
+        }
+        set_time_limit(3600);
+        list($appid, $appkey) = [sysconf('wechat_thr_appid'), sysconf('wechat_thr_appkey')];
+        $token = strtolower("{$name}-{$appid}-{$appkey}-{$type}");
+        if (class_exists('Yar_Client')) {
+            return new \Yar_Client(config('wechat.service_url') . "/service/api.client/yar/{$token}");
+        }
+        if (class_exists('SoapClient')) {
+            $location = config('wechat.service_url') . "/service/api.client/soap/{$token}";
+            return new \SoapClient(null, ['uri' => strtolower($name), 'location' => $location]);
+        }
+        throw new \think\Exception("Yar or soap extensions are not installed.");
+    }
+
+    /**
+     * 获取微信网页JSSDK
+     * @param string $url JS签名地址
+     * @return array
+     * @throws \WeChat\Exceptions\InvalidResponseException
+     * @throws \WeChat\Exceptions\LocalCacheException
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function getWebJssdkSign($url = null)
+    {
+        $url = is_null($url) ? request()->url(true) : $url;
+        if (self::getType() === 'api') {
+            return self::WeChatScript()->getJsSign($url);
+        } else {
+            return self::wechat()->jsSign($url);
+        }
+    }
+
+    /**
+     * 初始化进入授权
+     * @param string $url 授权页面URL地址
+     * @param integer $isfull 授权公众号模式
+     * @param boolean $isRedirect 是否进行跳转
+     * @return array
+     * @throws \WeChat\Exceptions\InvalidResponseException
+     * @throws \WeChat\Exceptions\LocalCacheException
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function getWebOauthInfo($url, $isfull = 0, $isRedirect = true)
+    {
+        $appid = self::getAppid();
+        list($openid, $fansinfo) = [session("{$appid}_openid"), session("{$appid}_fansinfo")];
+        if ((empty($isfull) && !empty($openid)) || (!empty($isfull) && !empty($openid) && !empty($fansinfo))) {
+            empty($fansinfo) || FansService::set($fansinfo);
+            return ['openid' => $openid, 'fansinfo' => $fansinfo];
+        }
+        if (self::getType() === 'api') {
+            $wechat = self::WeChatOauth();
+            if (request()->get('state') !== $appid) {
+                $snsapi = empty($isfull) ? 'snsapi_base' : 'snsapi_userinfo';
+                $param = (strpos($url, '?') !== false ? '&' : '?') . 'rcode=' . encode($url);
+                $OauthUrl = $wechat->getOauthRedirect($url . $param, $appid, $snsapi);
+                if ($isRedirect) redirect($OauthUrl, [], 301)->send();
+                exit("window.location.href='{$OauthUrl}'");
+            }
+            if (($token = $wechat->getOauthAccessToken()) && isset($token['openid'])) {
+                session("{$appid}_openid", $openid = $token['openid']);
+                if (empty($isfull) && request()->get('rcode')) {
+                    redirect(decode(request()->get('rcode')), [], 301)->send();
+                }
+                session("{$appid}_fansinfo", $fansinfo = $wechat->getUserInfo($token['access_token'], $openid));
+                empty($fansinfo) || FansService::set($fansinfo);
+            }
+            redirect(decode(request()->get('rcode')), [], 301)->send();
+        } else {
+            $result = self::wechat()->oauth(session_id(), $url, $isfull);
+            session("{$appid}_openid", $openid = $result['openid']);
+            session("{$appid}_fansinfo", $fansinfo = $result['fans']);
+            if ((empty($isfull) && !empty($openid)) || (!empty($isfull) && !empty($openid) && !empty($fansinfo))) {
+                empty($fansinfo) || FansService::set($fansinfo);
+                return ['openid' => $openid, 'fansinfo' => $fansinfo];
+            }
+            if ($isRedirect && !empty($result['url'])) {
+                redirect($result['url'], [], 301)->send();
+            }
+            exit("window.location.href='{$result['url']}'");
+        }
+    }
+
+    /**
+     * 获取当前公众号APPID
+     * @return bool|string
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function getAppid()
+    {
+        if (self::getType() === 'api') {
+            return sysconf('wechat_appid');
+        } else {
+            return sysconf('wechat_thr_appid');
+        }
+    }
+
+    /**
+     * 获取接口授权模式
+     * @return string
+     * @throws \think\Exception
+     * @throws \think\exception\PDOException
+     */
+    public static function getType()
+    {
+        $type = strtolower(sysconf('wechat_type'));
+        if (in_array($type, ['api', 'thr'])) return $type;
+        throw new \think\Exception('请在后台配置微信对接授权模式');
+    }
+
+}

+ 1 - 1
vendor/autoload.php

@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInit8651ef010a748f71dab0ecc2b38ca5a2::getLoader();
+return ComposerAutoloaderInit8221d9d28aec2b292a3f5c4c16a2acdf::getLoader();

+ 7 - 7
vendor/composer/autoload_real.php

@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInit8651ef010a748f71dab0ecc2b38ca5a2
+class ComposerAutoloaderInit8221d9d28aec2b292a3f5c4c16a2acdf
 {
     private static $loader;
 
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit8651ef010a748f71dab0ecc2b38ca5a2
             return self::$loader;
         }
 
-        spl_autoload_register(array('ComposerAutoloaderInit8651ef010a748f71dab0ecc2b38ca5a2', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInit8221d9d28aec2b292a3f5c4c16a2acdf', 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        spl_autoload_unregister(array('ComposerAutoloaderInit8651ef010a748f71dab0ecc2b38ca5a2', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit8221d9d28aec2b292a3f5c4c16a2acdf', 'loadClassLoader'));
 
         $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
         if ($useStaticLoader) {
             require_once __DIR__ . '/autoload_static.php';
 
-            call_user_func(\Composer\Autoload\ComposerStaticInit8651ef010a748f71dab0ecc2b38ca5a2::getInitializer($loader));
+            call_user_func(\Composer\Autoload\ComposerStaticInit8221d9d28aec2b292a3f5c4c16a2acdf::getInitializer($loader));
         } else {
             $map = require __DIR__ . '/autoload_namespaces.php';
             foreach ($map as $namespace => $path) {
@@ -48,19 +48,19 @@ class ComposerAutoloaderInit8651ef010a748f71dab0ecc2b38ca5a2
         $loader->register(true);
 
         if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInit8651ef010a748f71dab0ecc2b38ca5a2::$files;
+            $includeFiles = Composer\Autoload\ComposerStaticInit8221d9d28aec2b292a3f5c4c16a2acdf::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequire8651ef010a748f71dab0ecc2b38ca5a2($fileIdentifier, $file);
+            composerRequire8221d9d28aec2b292a3f5c4c16a2acdf($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
 
-function composerRequire8651ef010a748f71dab0ecc2b38ca5a2($fileIdentifier, $file)
+function composerRequire8221d9d28aec2b292a3f5c4c16a2acdf($fileIdentifier, $file)
 {
     if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
         require $file;

+ 4 - 4
vendor/composer/autoload_static.php

@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInit8651ef010a748f71dab0ecc2b38ca5a2
+class ComposerStaticInit8221d9d28aec2b292a3f5c4c16a2acdf
 {
     public static $files = array (
         '841780ea2e1d6545ea3a253239d59c05' => __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu/functions.php',
@@ -346,9 +346,9 @@ class ComposerStaticInit8651ef010a748f71dab0ecc2b38ca5a2
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInit8651ef010a748f71dab0ecc2b38ca5a2::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInit8651ef010a748f71dab0ecc2b38ca5a2::$prefixDirsPsr4;
-            $loader->classMap = ComposerStaticInit8651ef010a748f71dab0ecc2b38ca5a2::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInit8221d9d28aec2b292a3f5c4c16a2acdf::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit8221d9d28aec2b292a3f5c4c16a2acdf::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInit8221d9d28aec2b292a3f5c4c16a2acdf::$classMap;
 
         }, null, ClassLoader::class);
     }