Browse Source

fix:按照前端修改接口

super-yimizi 1 month ago
parent
commit
e23b6c44cf
63 changed files with 2692 additions and 163 deletions
  1. 1 1
      addons/epay/.addonrc
  2. 0 0
      addons/epay/certs/public_key.pem
  3. 143 64
      addons/epay/config.html
  4. 2 2
      addons/epay/info.ini
  5. 11 6
      addons/epay/library/RedirectResponse.php
  6. 8 3
      addons/epay/library/Service.php
  7. 1 1
      application/admin/controller/Ajax.php
  8. 30 0
      application/admin/controller/Epay.php
  9. 1 1
      application/admin/controller/shop/Goods.php
  10. 172 0
      application/admin/controller/shop/GoodsLabel.php
  11. 60 0
      application/admin/controller/shop/GoodsLabelGroup.php
  12. 38 0
      application/admin/controller/shop/Unit.php
  13. 39 0
      application/admin/controller/supplier/Category.php
  14. 68 0
      application/admin/controller/supplier/Index.php
  15. 1 0
      application/admin/lang/zh-cn/shop/goods.php
  16. 23 0
      application/admin/lang/zh-cn/shop/goods_label.php
  17. 14 0
      application/admin/lang/zh-cn/shop/goods_label_group.php
  18. 10 0
      application/admin/lang/zh-cn/shop/unit.php
  19. 10 0
      application/admin/lang/zh-cn/supplier/category.php
  20. 24 0
      application/admin/lang/zh-cn/supplier/index.php
  21. 56 0
      application/admin/model/shop/GoodsLabel.php
  22. 33 0
      application/admin/model/shop/GoodsLabelGroup.php
  23. 60 0
      application/admin/model/shop/Unit.php
  24. 50 0
      application/admin/model/supplier/Category.php
  25. 55 0
      application/admin/model/supplier/Index.php
  26. 27 0
      application/admin/validate/shop/Unit.php
  27. 27 0
      application/admin/validate/supplier/Category.php
  28. 27 0
      application/admin/validate/supplier/Index.php
  29. 57 7
      application/admin/view/shop/goods/add.html
  30. 9 3
      application/admin/view/shop/goods/edit.html
  31. 5 4
      application/admin/view/shop/goods/index.html
  32. 114 0
      application/admin/view/shop/goods_label/add.html
  33. 112 0
      application/admin/view/shop/goods_label/edit.html
  34. 29 0
      application/admin/view/shop/goods_label/index.html
  35. 32 0
      application/admin/view/shop/goods_label_group/add.html
  36. 32 0
      application/admin/view/shop/goods_label_group/edit.html
  37. 29 0
      application/admin/view/shop/goods_label_group/index.html
  38. 3 3
      application/admin/view/shop/theme/index.html
  39. 33 0
      application/admin/view/shop/unit/add.html
  40. 33 0
      application/admin/view/shop/unit/edit.html
  41. 46 0
      application/admin/view/shop/unit/index.html
  42. 25 0
      application/admin/view/shop/unit/recyclebin.html
  43. 32 0
      application/admin/view/supplier/category/add.html
  44. 32 0
      application/admin/view/supplier/category/edit.html
  45. 29 0
      application/admin/view/supplier/category/index.html
  46. 25 0
      application/admin/view/supplier/category/recyclebin.html
  47. 117 0
      application/admin/view/supplier/index/add.html
  48. 119 0
      application/admin/view/supplier/index/edit.html
  49. 29 0
      application/admin/view/supplier/index/index.html
  50. 25 0
      application/admin/view/supplier/index/recyclebin.html
  51. 5 0
      application/api/controller/Goods.php
  52. 19 0
      application/common/Enum/GoodsLabelEnum.php
  53. 29 29
      application/common/Service/lottery/LotteryChanceService.php
  54. BIN
      public/assets/img/goods/sales-exact.png
  55. BIN
      public/assets/img/goods/sales-sketchy.png
  56. BIN
      public/assets/img/goods/stock-exact.png
  57. BIN
      public/assets/img/goods/stock-sketchy.png
  58. 57 39
      public/assets/js/backend/shop/goods.js
  59. 118 0
      public/assets/js/backend/shop/goods_label.js
  60. 58 0
      public/assets/js/backend/shop/goods_label_group.js
  61. 131 0
      public/assets/js/backend/shop/unit.js
  62. 131 0
      public/assets/js/backend/supplier/category.js
  63. 186 0
      public/assets/js/backend/supplier/index.js

+ 1 - 1
addons/epay/.addonrc

@@ -1 +1 @@
-{"files":["application\/admin\/controller\/Epay.php","public\/assets\/addons\/epay\/css\/common.css","public\/assets\/addons\/epay\/less\/common.less","public\/assets\/addons\/epay\/images\/paid.png","public\/assets\/addons\/epay\/images\/logo-wechat.png","public\/assets\/addons\/epay\/images\/logo-alipay.png","public\/assets\/addons\/epay\/images\/screenshot-alipay.png","public\/assets\/addons\/epay\/images\/alipay.png","public\/assets\/addons\/epay\/images\/screenshot-wechat.png","public\/assets\/addons\/epay\/images\/wechat.png","public\/assets\/addons\/epay\/images\/scan.png","public\/assets\/addons\/epay\/images\/expired.png","public\/assets\/addons\/epay\/js\/jquery.qrcode.min.js","public\/assets\/addons\/epay\/js\/common.js"],"license":"regular","licenseto":"9671","licensekey":"fY2u0NovbBezQWUn vj6BNKi5JAcnHiwqWlIMgA==","domains":[],"licensecodes":[],"validations":[]}
+{"files":["application\/admin\/controller\/Epay.php","public\/assets\/addons\/epay\/css\/common.css","public\/assets\/addons\/epay\/less\/common.less","public\/assets\/addons\/epay\/images\/paid.png","public\/assets\/addons\/epay\/images\/logo-wechat.png","public\/assets\/addons\/epay\/images\/logo-alipay.png","public\/assets\/addons\/epay\/images\/screenshot-alipay.png","public\/assets\/addons\/epay\/images\/alipay.png","public\/assets\/addons\/epay\/images\/screenshot-wechat.png","public\/assets\/addons\/epay\/images\/wechat.png","public\/assets\/addons\/epay\/images\/scan.png","public\/assets\/addons\/epay\/images\/expired.png","public\/assets\/addons\/epay\/js\/jquery.qrcode.min.js","public\/assets\/addons\/epay\/js\/common.js"],"license":"regular","licenseto":"9671","licensekey":"w2ROCdQ79K8Hjhls WW4vyNH\/8Ynuq5XM75e6Jw==","domains":[],"licensecodes":[],"validations":[]}

+ 0 - 0
addons/epay/certs/public_key.pem


+ 143 - 64
addons/epay/config.html

@@ -23,7 +23,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][appid]" value="{$item.value.appid|default=''}" class="form-control" data-rule="" data-tip="APP应用中支付时使用"/>
+                                        <input type="text" name="row[wechat][appid]" value="{$item.value.appid|default=''}" class="form-control" data-rule="appid" data-tip="APP应用中支付时使用"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -34,7 +34,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip="公众号中支付时使用"/>
+                                        <input type="text" name="row[wechat][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="appid" data-tip="公众号中支付时使用"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -45,7 +45,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][app_secret]" value="{$item.value.app_secret|default=''}" class="form-control" data-rule="" data-tip="公众号中支付时使用"/>
+                                        <input type="text" name="row[wechat][app_secret]" value="{$item.value.app_secret|default=''}" class="form-control" data-rule="keysecret" data-tip="公众号中支付时使用"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -56,7 +56,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][miniapp_id]" value="{$item.value.miniapp_id|default=''}" class="form-control" data-rule="" data-tip="仅在小程序支付时使用"/>
+                                        <input type="text" name="row[wechat][miniapp_id]" value="{$item.value.miniapp_id|default=''}" class="form-control" data-rule="appid" data-tip="仅在小程序支付时使用"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -67,29 +67,105 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][mch_id]" value="{$item.value.mch_id|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                        <input type="number" name="row[wechat][mch_id]" value="{$item.value.mch_id|default=''}" class="form-control" data-rule="mchid" data-tip=""/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
                             </td>
                         </tr>
                         <tr>
-                            <td>微信支付商户API密钥V2</td>
+                            <td>商户APIv2密钥</td>
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][key]" value="{$item.value.key|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                        <input type="text" name="row[wechat][key]" value="{$item.value.key|default=''}" class="form-control" data-rule="keysecret" data-tip="仅用于微信支付V2接口"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
                             </td>
                         </tr>
                         <tr>
-                            <td>微信支付商户API密钥V3</td>
+                            <td>V3配置</td>
+                            <td>
+                                <table class="table  table-config mb-0">
+                                    <tbody>
+                                    <!-- V3 特有字段 -->
+                                    <tr>
+                                        <td>商户APIv3密钥</td>
+                                        <td>
+                                            <div class="row">
+                                                <div class="col-sm-7 col-xs-12">
+                                                    <input type="text" name="row[wechat][key_v3]" value="{$item.value.key_v3|default=''}" class="form-control" data-rule="keysecret" data-tip=""/>
+                                                </div>
+                                                <div class="col-sm-4"></div>
+                                            </div>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>公钥ID</td>
+                                        <td>
+                                            <div class="row">
+                                                <div class="col-sm-7 col-xs-12">
+                                                    <input type="text" name="row[wechat][public_key_id]" value="{$item.value.public_key_id|default=''}" class="form-control" data-rule="public_key_id" data-tip=""/>
+                                                </div>
+                                                <div class="col-sm-4"></div>
+                                            </div>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>公钥证书路径</td>
+                                        <td>
+                                            <div class="row">
+                                                <div class="col-sm-7 col-xs-12">
+                                                    <div class="input-group">
+                                                        <input id="c-public_key" class="form-control" size="50" name="row[wechat][public_key]" type="text" value="{$item.value.public_key|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
+                                                        <div class="input-group-addon no-border no-padding">
+                                                            <span><button type="button" id="faupload-public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"public_key"}' data-mimetype="pem" data-input-id="c-public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                                        </div>
+                                                        <span class="msg-box n-right" for="c-public_key"></span>
+                                                    </div>
+                                                    <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com/doc/v3/partner/4012925323" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付公钥证书?</a></div>
+                                                </div>
+                                                <div class="col-sm-4"></div>
+                                            </div>
+                                        </td>
+                                    </tr>
+                                    </tbody>
+                                </table>
+                            </td>
+                        </tr>
+
+                        <tr>
+                            <td>商户API证书cert</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <div class="input-group">
+                                            <input id="c-cert_client" class="form-control" size="50" name="row[wechat][cert_client]" type="text" value="{$item.value.cert_client|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
+                                            <div class="input-group-addon no-border no-padding">
+                                                <span><button type="button" id="faupload-cert_client" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_client"}' data-mimetype="pem" data-input-id="c-cert_client" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                            </div>
+                                            <span class="msg-box n-right" for="c-cert_client"></span>
+                                        </div>
+                                        <div style="margin-top:5px;"><a href="https://kf.qq.com/faq/161222NneAJf161222U7fARv.html" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付商户API证书?</a></div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>商户API证书key</td>
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][key_v3]" value="{$item.value.key_v3|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                        <div class="input-group">
+                                            <input id="c-cert_key" class="form-control" size="50" name="row[wechat][cert_key]" type="text" value="{$item.value.cert_key|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
+                                            <div class="input-group-addon no-border no-padding">
+                                                <span><button type="button" id="faupload-cert_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_key"}' data-mimetype="pem" data-input-id="c-cert_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                            </div>
+                                            <span class="msg-box n-right" for="c-cert_key"></span>
+                                        </div>
+                                        <div style="margin-top:5px;"><a href="https://kf.qq.com/faq/161222NneAJf161222U7fARv.html" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付商户API证书?</a></div>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -114,7 +190,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][sub_mch_id]" value="{$item.value.sub_mch_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                        <input type="text" name="row[wechat][sub_mch_id]" value="{$item.value.sub_mch_id|default=''}" class="form-control" data-rule="mchid" data-tip="如果未用到子商户,请勿填写"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -125,7 +201,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][sub_appid]" value="{$item.value.sub_appid|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                        <input type="text" name="row[wechat][sub_appid]" value="{$item.value.sub_appid|default=''}" class="form-control" data-rule="appid" data-tip="如果未用到子商户,请勿填写"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -136,7 +212,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][sub_app_id]" value="{$item.value.sub_app_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                        <input type="text" name="row[wechat][sub_app_id]" value="{$item.value.sub_app_id|default=''}" class="form-control" data-rule="appid" data-tip="如果未用到子商户,请勿填写"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -147,7 +223,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[wechat][sub_miniapp_id]" value="{$item.value.sub_miniapp_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                        <input type="text" name="row[wechat][sub_miniapp_id]" value="{$item.value.sub_miniapp_id|default=''}" class="form-control" data-rule="appid" data-tip="如果未用到子商户,请勿填写"/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -164,42 +240,8 @@
                                 </div>
                             </td>
                         </tr>
-                        <tr>
-                            <td>微信支付API证书cert</td>
-                            <td>
-                                <div class="row">
-                                    <div class="col-sm-8 col-xs-12">
-                                        <div class="input-group">
-                                            <input id="c-cert_client" class="form-control" size="50" name="row[wechat][cert_client]" type="text" value="{$item.value.cert_client|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
-                                            <div class="input-group-addon no-border no-padding">
-                                                <span><button type="button" id="faupload-cert_client" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_client"}' data-mimetype="pem" data-input-id="c-cert_client" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
-                                            </div>
-                                            <span class="msg-box n-right" for="c-cert_client"></span>
-                                        </div>
-                                        <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
-                                    </div>
-                                    <div class="col-sm-4"></div>
-                                </div>
-                            </td>
-                        </tr>
-                        <tr>
-                            <td>微信支付API证书key</td>
-                            <td>
-                                <div class="row">
-                                    <div class="col-sm-8 col-xs-12">
-                                        <div class="input-group">
-                                            <input id="c-cert_key" class="form-control" size="50" name="row[wechat][cert_key]" type="text" value="{$item.value.cert_key|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
-                                            <div class="input-group-addon no-border no-padding">
-                                                <span><button type="button" id="faupload-cert_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_key"}' data-mimetype="pem" data-input-id="c-cert_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
-                                            </div>
-                                            <span class="msg-box n-right" for="c-cert_key"></span>
-                                        </div>
-                                        <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
-                                    </div>
-                                    <div class="col-sm-4"></div>
-                                </div>
-                            </td>
-                        </tr>
+
+
 
                         <tr>
                             <td>记录日志</td>
@@ -239,7 +281,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[alipay][pid]" value="{$item.value.pid|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                        <input type="number" name="row[alipay][pid]" value="{$item.value.pid|default=''}" class="form-control" data-rule="pid" data-tip=""/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -250,7 +292,7 @@
                             <td>
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[alipay][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                        <input type="number" name="row[alipay][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="pid" data-tip=""/>
                                     </div>
                                     <div class="col-sm-4"></div>
                                 </div>
@@ -279,18 +321,6 @@
                             </td>
                         </tr>
                         <tr>
-                            <td>应用私钥(private_key)</td>
-                            <td>
-                                <div class="row">
-                                    <div class="col-sm-8 col-xs-12">
-                                        <input type="text" name="row[alipay][private_key]" value="{$item.value.private_key|default=''}" class="form-control" data-rule=""/>
-                                        <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用私钥?</a></div>
-                                    </div>
-                                    <div class="col-sm-4"></div>
-                                </div>
-                            </td>
-                        </tr>
-                        <tr>
                             <td>签名方式</td>
                             <td>
                                 <div>
@@ -320,7 +350,7 @@
                                 <div class="row">
                                     <div class="col-sm-8 col-xs-12">
                                         <div class="input-group">
-                                            <input id="c-ali_public_key" class="form-control" size="50" name="row[alipay][ali_public_key]" type="text" value="{$item.value.ali_public_key|default=''|htmlentities}" placeholder="普通公钥请直接粘贴,公钥证书请点击右侧的上传">
+                                            <input id="c-ali_public_key" class="form-control" size="50" name="row[alipay][ali_public_key]" type="text" value="{$item.value.ali_public_key|default=''|htmlentities}" placeholder="普通公钥请直接粘贴,公钥证书请点击上传">
                                             <div class="input-group-addon no-border no-padding {if ($item.value.signtype??'')==='publickey' || ($item.value.signtype??'')==''}hidden{/if}" data-signtype="cert">
                                                 <span><button type="button" id="faupload-ali_public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"ali_public_key"}' data-mimetype="crt" data-input-id="c-ali_public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
                                             </div>
@@ -369,6 +399,25 @@
                                 </div>
                             </td>
                         </tr>
+                        <tr>
+                            <td>应用私钥(private_key)</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[alipay][private_key]" value="{$item.value.private_key|default=''}" class="form-control" data-rule=""/>
+                                        <div style="margin-top:5px;">
+                                            <span data-signtype="publickey" class="{if ($item.value.signtype??'')==='cert'}hidden{/if}">
+                                                <a href="https://opensupport.alipay.com/support/FAQ/671860f40f6363044540b717prod" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用私钥?</a>
+                                            </span>
+                                            <span data-signtype="cert" class="{if ($item.value.signtype??'')==='publickey' || ($item.value.signtype??'')==''}hidden{/if}">
+                                                <a href="https://opensupport.alipay.com/support/FAQ/6718ab4563fae8044fe13dc7prod" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用私钥?</a>
+                                            </span>
+                                        </div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
 
                         <tr>
                             <td>记录日志</td>
@@ -414,6 +463,36 @@
         define('backend/addon', ['backend', 'form'], function (Backend, Form) {
             var Controller = {
                 config: function () {
+                    $.validator.config({
+                        rules: {
+                            appid: function (element) {
+                                if (!element.value.toString().match(/^wx[a-zA-Z0-9]{16}$/)) {
+                                    return __('必须以wx开头,且长度为18位');
+                                }
+                            },
+                            keysecret: function (element) {
+                                if (!element.value.toString().match(/^[a-zA-Z0-9]{32}$/)) {
+                                    return __('长度为32位,且为字母数字');
+                                }
+                            },
+                            public_key_id: function (element) {
+                                if (!element.value.toString().match(/^PUB_KEY_ID_[0-9]{34}$/)) {
+                                    return __('必须以PUB_KEY_ID_开头,长度为45位');
+                                }
+                            },
+                            mchid: function (element) {
+                                if (!element.value.toString().match(/^[0-9]{8,32}$/)) {
+                                    return __('长度为至少8位,且为数字');
+                                }
+                            },
+                            pid: function (element) {
+                                if (!element.value.toString().match(/^[0-9]{16}$/)) {
+                                    return __('长度为16位,且为数字');
+                                }
+                            },
+                        }
+                    });
+
                     $(document).on("click", ".nav-group li a[data-toggle='tab']", function () {
                         if ($(this).attr("href") === "#all") {
                             $(".tab-pane").addClass("active in");

+ 2 - 2
addons/epay/info.ini

@@ -1,9 +1,9 @@
 name = epay
 title = 微信支付宝整合
-intro = 可用于快速整合企业微信、支付宝支付功能
+intro = 可用于快速整合微信支付、支付宝支付功能
 author = FastAdmin
 website = https://www.fastadmin.net
-version = 1.3.10
+version = 1.3.13
 state = 1
 url = /addons/epay
 license = regular

+ 11 - 6
addons/epay/library/RedirectResponse.php

@@ -2,7 +2,10 @@
 
 namespace addons\epay\library;
 
-class RedirectResponse extends \Symfony\Component\HttpFoundation\RedirectResponse implements \JsonSerializable, \Serializable
+use Symfony\Component\HttpFoundation\RedirectResponse as BaseRedirectResponse;
+use JsonSerializable;
+
+class RedirectResponse extends BaseRedirectResponse implements JsonSerializable
 {
     public function __toString()
     {
@@ -41,18 +44,20 @@ class RedirectResponse extends \Symfony\Component\HttpFoundation\RedirectRespons
         return $this;
     }
 
-    public function jsonSerialize()
+    #[\ReturnTypeWillChange]
+    public function jsonSerialize(): mixed
     {
         return $this->getContent();
     }
 
-    public function serialize()
+    // 使用 PHP 8 兼容的新序列化方式
+    public function __serialize(): array
     {
-        return serialize($this->content);
+        return ['content' => $this->content];
     }
 
-    public function unserialize($serialized)
+    public function __unserialize(array $data): void
     {
-        return $this->content = unserialize($serialized);
+        $this->content = $data['content'] ?? '';
     }
 }

+ 8 - 3
addons/epay/library/Service.php

@@ -319,8 +319,8 @@ class Service
 
     /**
      * 处理证书路径
-     * @param array $config 配置
-     * @param string $field 字段
+     * @param array  $config 配置
+     * @param string $field  字段
      * @return void
      */
     private static function processAddonsPath(&$config, $field)
@@ -346,7 +346,7 @@ class Service
 
         // 处理微信证书路径
         if ($type === 'wechat') {
-            $certFields = ['cert_client', 'cert_key'];
+            $certFields = ['cert_client', 'cert_key', 'public_key'];
             foreach ($certFields as $field) {
                 self::processAddonsPath($config, $field);
             }
@@ -378,6 +378,11 @@ class Service
                 $config['mch_secret_cert'] = $config['cert_key'];
                 $config['mch_public_cert_path'] = $config['cert_client'];
 
+                // 配置微信支付公钥ID及证书路径
+                $config['wechat_public_cert_path'] = [
+                    ($config['public_key_id'] ?? '') => ($config['public_key'] ?? '')
+                ];
+
                 $config['sub_mp_app_id'] = $config['sub_appid'] ?? '';
                 $config['sub_app_id'] = $config['sub_app_id'] ?? '';
                 $config['sub_mini_app_id'] = $config['sub_miniapp_id'] ?? '';

+ 1 - 1
application/admin/controller/Ajax.php

@@ -304,7 +304,7 @@ class Ajax extends Backend
                 $where['level'] = 3;
             }
         }
-        $provincelist = Db::name('area')->where($where)->field('id as value,name')->select();
+        $provincelist = Db::name('shop_area')->where($where)->field('id as value,name')->select();
         $this->success('', '', $provincelist);
     }
 

+ 30 - 0
application/admin/controller/Epay.php

@@ -3,6 +3,8 @@
 namespace app\admin\controller;
 
 use app\common\controller\Backend;
+use app\common\exception\UploadException;
+use app\common\library\Upload;
 use think\Config;
 
 class Epay extends Backend
@@ -17,10 +19,16 @@ class Epay extends Backend
     {
         Config::set('default_return_type', 'json');
 
+        //检测是否有addon/config的权限
+        if (!$this->auth->check('addon/config')) {
+            $this->error('暂无权限');
+        }
+
         $certname = $this->request->post('certname', '');
         $certPathArr = [
             'cert_client'         => '/addons/epay/certs/apiclient_cert.pem', //微信支付api
             'cert_key'            => '/addons/epay/certs/apiclient_key.pem', //微信支付api
+            'public_key'          => '/addons/epay/certs/public_key.pem', //微信公钥证书
             'app_cert_public_key' => '/addons/epay/certs/appCertPublicKey.crt',//应用公钥证书路径
             'alipay_root_cert'    => '/addons/epay/certs/alipayRootCert.crt', //支付宝根证书路径
             'ali_public_key'      => '/addons/epay/certs/alipayCertPublicKey.crt', //支付宝公钥证书路径
@@ -33,7 +41,29 @@ class Epay extends Backend
         if (!$file) {
             $this->error("未上传文件");
         }
+
+        //验证文件大小限制和后缀限制
+        if (!$file->check([
+            'size' => $this->convertToBytes(config('upload.maxsize')),
+            'ext'  => 'pem,crt',
+        ])) {
+            $this->error($file->getError());
+        }
+
+        //验证上传的文件内容是否符合pem,crt格式内容
+        $fileContent = file_get_contents($file->getInfo('tmp_name'));
+        if (!preg_match('/-----BEGIN(.*)-----[\s\S]+-----END(.*)-----/', $fileContent)) {
+            $this->error("文件内容错误");
+        }
+
         $file->move(dirname(ROOT_PATH . $url), basename(ROOT_PATH . $url), true);
         $this->success(__('上传成功'), '', ['url' => $url]);
     }
+
+    protected function convertToBytes($size)
+    {
+        $units = ['b' => 1, 'k' => 1024, 'kb' => 1024, 'm' => 1048576, 'mb' => 1048576, 'g' => 1073741824, 'gb' => 1073741824];
+        preg_match('/^(\d+(?:\.\d+)?)\s*([a-z]+)$/i', strtolower(trim($size)), $matches);
+        return intval($matches[1] * $units[$matches[2]]);
+    }
 }

+ 1 - 1
application/admin/controller/shop/Goods.php

@@ -589,7 +589,7 @@ class Goods extends Backend
      */
     public function edit($ids = null)
     {
-        $row = $this->model->get($ids);
+        $row = $this->model->find($ids);
         if (!$row) {
             $this->error(__('No Results were found'));
         }

+ 172 - 0
application/admin/controller/shop/GoodsLabel.php

@@ -0,0 +1,172 @@
+<?php
+
+namespace app\admin\controller\shop;
+
+use app\common\controller\Backend;
+use think\Db;
+use think\exception\PDOException;
+use think\exception\ValidateException;
+use think\Exception;
+class GoodsLabel extends Backend
+{
+    protected $model = null;
+    protected $relationSearch = true;
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\shop\GoodsLabel;
+        $this->view->assign("statusList", $this->model->getStatusList());
+        $this->assignconfig("statusList", json_encode($this->model->getStatusList()));
+        $this->view->assign("styleTypeList", $this->model->getStyleTypeList());
+        $this->assignconfig("styleTypeList", json_encode($this->model->getStyleTypeList()));
+    }
+
+    public function getList()
+    {
+        $list = $this->model->field('id,name')->select();
+        return json($list);
+    }
+
+    public function index()
+    {
+        $this->request->filter(['strip_tags', 'trim']);
+        if ($this->request->isAjax()) {
+            if ($this->request->request('keyField')) {
+                return $this->selectpage();
+            }
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+
+            $list = $this->model
+                ->with(['group'])
+                ->where($where)
+                ->order($sort, $order)
+                ->paginate($limit);
+
+            foreach ($list as $row) {
+                $row->visible(['id', 'name', 'group_id', 'style_type', 'color_json', 'icon', 'memo', 'weigh', 'status', 'createtime', 'updatetime']);
+                $row->visible(['status_text', 'style_type_text','group.name']);
+                $row->hidden(['deletetime']);
+            }
+
+            $result = array("total" => $list->total(), "rows" => $list->items());
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+
+      /**
+     * 添加
+     */
+    public function add()
+    {
+        if ($this->request->isPost()) {
+            $params = $this->request->post('row/a');
+            if ($params) {
+                if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
+                    $params[$this->dataLimitField] = $this->auth->id;
+                }
+                // 检查标签名称是否唯一
+                $label = $this->model->where('name', $params['name'])->find();
+                if ($label) {
+                    $this->error('标签名称已存在');
+                }
+                $result = false;
+                Db::startTrans();
+                try {
+                    //是否采用模型验证
+                    if ($this->modelValidate) {
+                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
+                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
+                        $this->model->validateFailException(true)->validate($validate);
+                    }
+                    $result = $this->model->allowField(true)->save($params);
+                    Db::commit();
+                } catch (ValidateException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success('添加成功');
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        return $this->view->fetch();
+    }
+
+
+    /**
+     * 编辑
+     */
+    public function edit($ids = null)
+    {
+        $row = $this->model->get($ids);
+        if (!$row) {
+            $this->error(__('No Results were found'));
+        }
+        $adminIds = $this->getDataLimitAdminIds();
+        if (is_array($adminIds)) {
+            if (!in_array($row[$this->dataLimitField], $adminIds)) {
+                $this->error(__('You have no permission'));
+            }
+        }
+        if ($this->request->isPost()) {
+            $params = $this->request->post('row/a');
+            if ($params) {
+                // 检查标签名称是否唯一
+                $label = $this->model->where('name', $params['name'])->where('id', 'neq', $row->id)->find();
+                if ($label) {
+                    $this->error('标签名称已存在');
+                }
+                $params = $this->preExcludeFields($params);
+                $result = false;
+                Db::startTrans();
+                try {
+                    //是否采用模型验证
+                    if ($this->modelValidate) {
+                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
+                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
+                        $row->validateFailException(true)->validate($validate);
+                    }
+                    $result = $row->allowField(true)->save($params);
+                    Db::commit();
+                } catch (ValidateException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success('编辑成功');
+                } else {
+                    $this->error(__('No rows were updated'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        $this->view->assign("row", $row);
+        return $this->view->fetch();
+    }
+
+    public function checkLabelNameUnique()
+    {
+        $name = $this->request->post('name');
+        $id = $this->request->post('id');
+        $count = $this->model->where('name', $name)->where('id', 'neq', $id)->count();
+        return $count > 0 ? '标签名称已存在' : '标签名称可用';
+    }
+
+}

+ 60 - 0
application/admin/controller/shop/GoodsLabelGroup.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace app\admin\controller\shop;
+
+use app\common\controller\Backend;
+use think\Db;
+use think\exception\PDOException;
+use think\Exception;
+use think\exception\ValidateException;
+class GoodsLabelGroup extends Backend
+{
+    /**
+     * GoodsLabelGroup模型对象
+     * @var \app\admin\model\shop\GoodsLabelGroup
+     */
+    protected $model = null;
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\shop\GoodsLabelGroup;
+        $this->view->assign("statusList", $this->model->getStatusList());
+        $this->assignconfig("statusList", json_encode($this->model->getStatusList()));
+    }
+
+    public function getList()
+    {
+        $list = $this->model->field('id,name')->select();
+        return json($list);
+    }
+
+    public function index()
+    {
+        $this->relationSearch = false;
+        $this->request->filter(['strip_tags', 'trim']);
+        if ($this->request->isAjax()) {
+            if ($this->request->request('keyField')) {
+                return $this->selectpage();
+            }
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+
+            $list = $this->model
+                ->where($where)
+                ->order($sort, $order)
+                ->paginate($limit);
+
+            foreach ($list as $row) {
+                $row->visible(['id', 'name', 'sort', 'status', 'createtime', 'updatetime']);
+                $row->visible(['status_text']);
+                $row->hidden(['deletetime']);
+            }
+
+            $result = array("total" => $list->total(), "rows" => $list->items());
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+  
+}

+ 38 - 0
application/admin/controller/shop/Unit.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace app\admin\controller\shop;
+
+use app\common\controller\Backend;
+
+/**
+ * 商品单位管理
+ *
+ * @icon fa fa-circle-o
+ */
+class Unit extends Backend
+{
+
+    /**
+     * Unit模型对象
+     * @var \app\admin\model\shop\Unit
+     */
+    protected $model = null;
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\shop\Unit;
+        $this->view->assign("statusList", $this->model->getStatusList());
+        $this->assignconfig("statusList", json_encode($this->model->getStatusList()));
+    }
+
+
+
+    /**
+     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
+     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
+     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
+     */
+
+
+}

+ 39 - 0
application/admin/controller/supplier/Category.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace app\admin\controller\supplier;
+
+use app\common\controller\Backend;
+use app\common\Enum\StatusEnum;
+/**
+ * 商品供应商分类
+ *
+ * @icon fa fa-circle-o
+ */
+class Category extends Backend
+{
+
+    /**
+     * Category模型对象
+     * @var \app\admin\model\supplier\Category
+     */
+    protected $model = null;
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new \app\admin\model\supplier\Category;
+        $this->view->assign("statusList", StatusEnum::getMap());
+        $this->assignconfig("statusList", json_encode(StatusEnum::getMap()));
+
+    }
+
+
+
+    /**
+     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
+     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
+     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
+     */
+
+
+}

+ 68 - 0
application/admin/controller/supplier/Index.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace app\admin\controller\supplier;
+
+use app\common\controller\Backend;
+use app\common\Enum\StatusEnum;
+use app\admin\model\supplier\Index as IndexModel;
+/**
+ * 商品供应商
+ *
+ * @icon fa fa-circle-o
+ */
+class Index extends Backend
+{
+
+    /**
+     * Index模型对象
+     * @var \app\admin\model\supplier\Index
+     */
+    protected $model = null;
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = new IndexModel;
+        $this->view->assign("statusList", StatusEnum::getMap());
+        $this->assignconfig("statusList", json_encode(StatusEnum::getMap()));
+
+    }
+
+    public function index()
+    {
+        $this->request->filter(['strip_tags', 'trim']);
+        if ($this->request->isAjax()) {
+            if ($this->request->request('keyField')) {
+                return $this->selectpage();
+            }
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+
+            $list = $this->model
+                ->with(['category'])
+                ->where($where)
+                ->order($sort, $order)
+                ->paginate($limit);
+
+            foreach ($list as $row) {
+                $row->visible(['id', 'name', 'category_id', 'contact', 'mobile', 'landline', 'weigh', 'status', 'createtime', 'updatetime']);
+                $row->visible(['status_text', 'category.name']);
+                $row->hidden(['deletetime']);
+            }
+
+            $result = array("total" => $list->total(), "rows" => $list->items());
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+
+
+
+    /**
+     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
+     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
+     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
+     */
+
+
+}

+ 1 - 0
application/admin/lang/zh-cn/shop/goods.php

@@ -5,6 +5,7 @@ return [
     'Type'                            => '商品类型',
     'Category_id'                     => '类别ID',
     'Category_ids'                    => '商品分类',
+    'Label_ids'                       => '商品标签',
     'Category'                        => '类别',
     'Goods_sn'                        => '商品编码',
     'Title'                           => '商品名称',

+ 23 - 0
application/admin/lang/zh-cn/shop/goods_label.php

@@ -0,0 +1,23 @@
+<?php
+
+return [
+    'Id'             => '标签ID',
+    'Name'           => '标签',
+    'Label group'       => '标签分组',
+    'Group_name'     => '标签分组',
+    'Style_type'     => '样式类型',
+    'Style_type diy' => '自定义',
+    'Style_type icon'=> '图片',
+    'Color_json'     => '颜色配置',
+    'Icon'           => '图标',
+    'Label memo'     => '标签备注',
+    'Memo'           => '标签备注',
+    'Weigh'          => '权重',
+    'Status'         => '状态',
+    'Status 0'       => '禁用',
+    'Status 1'       => '启用',
+    'Createtime'     => '创建时间',
+    'Updatetime'     => '更新时间',
+    'Deletetime'     => '删除时间',
+    'Operate'        => '操作',
+]; 

+ 14 - 0
application/admin/lang/zh-cn/shop/goods_label_group.php

@@ -0,0 +1,14 @@
+<?php
+
+return [
+    'Id'         => '分组ID',
+    'Name'       => '分组名称',
+    'Sort'       => '排序',
+    'Status'     => '状态',
+    'Status 0'   => '禁用',
+    'Status 1'   => '启用',
+    'Createtime' => '创建时间',
+    'Updatetime' => '更新时间',
+    'Deletetime' => '删除时间',
+    'Operate'    => '操作',
+]; 

+ 10 - 0
application/admin/lang/zh-cn/shop/unit.php

@@ -0,0 +1,10 @@
+<?php
+
+return [
+    'Name'       => '商品单位名称',
+    'Weigh'      => '排序',
+    'Status'     => '状态',
+    'Createtime' => '创建时间',
+    'Updatetime' => '更新时间',
+    'Deletetime' => '删除时间'
+];

+ 10 - 0
application/admin/lang/zh-cn/supplier/category.php

@@ -0,0 +1,10 @@
+<?php
+
+return [
+    'Name'       => '分类名称',
+    'Weigh'      => '排序',
+    'Status'     => '状态',
+    'Createtime' => '创建时间',
+    'Updatetime' => '更新时间',
+    'Deletetime' => '删除时间'
+];

+ 24 - 0
application/admin/lang/zh-cn/supplier/index.php

@@ -0,0 +1,24 @@
+<?php
+
+return [
+    'Name'                 => '名称',
+    'Code'                 => '供应商编码',
+    'Supplier_category_id' => '供应商分类',
+    'Contact'              => '联系人',
+    'Mobile'               => '联系电话',
+    'Landline'             => '座机号码',
+    'Email'                => '邮箱',
+    'Province_id'          => '省',
+    'City_id'              => '市',
+    'District_id'          => '区',
+    'Address'              => '详细地址',
+    'Bank_account'         => '银行账号',
+    'Bank'                 => '开户银行',
+    'Cardholder_name'      => '持卡人姓名',
+    'Tax_id'               => '税务登记号',
+    'Weigh'                => '排序',
+    'Status'               => '状态',
+    'Createtime'           => '创建时间',
+    'Updatetime'           => '更新时间',
+    'Deletetime'           => '删除时间'
+];

+ 56 - 0
application/admin/model/shop/GoodsLabel.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace app\admin\model\shop;
+
+use think\Model;
+use app\common\Enum\StatusEnum;
+use app\common\Enum\GoodsLabelEnum;
+class GoodsLabel extends Model
+{
+    protected $name = 'shop_goods_label';
+    protected $autoWriteTimestamp = 'int';
+    protected $createTime = 'createtime';
+    protected $updateTime = 'updatetime';
+    protected $deleteTime = 'deletetime';
+
+    protected $append = ['status_text', 'style_type_text'];
+
+    public function getStatusList()
+    {
+        return StatusEnum::getMap();
+    }
+
+    public function getStyleTypeList()
+    {
+        return GoodsLabelEnum::getMap();
+    }
+
+    public function getStatusTextAttr($value, $data)
+    {
+        $value = $value ?: ($data['status'] ?? '');
+        $list = $this->getStatusList();
+        return $list[$value] ?? '';
+    }
+
+    public function getStyleTypeTextAttr($value, $data)
+    {
+        $value = $value ?: ($data['style_type'] ?? '');
+        $list = $this->getStyleTypeList();
+        return $list[$value] ?? '';
+    }
+
+    public function group()
+    {
+        return $this->belongsTo('GoodsLabelGroup', 'group_id', 'id');
+    }
+
+    public function getColorJsonAttr($value)
+    {
+        return is_string($value) ? (json_decode($value, true) ?: []) : [];
+    }
+
+    public function setColorJsonAttr($value)
+    {
+        return is_array($value) ? json_encode($value) : $value;
+    }
+}

+ 33 - 0
application/admin/model/shop/GoodsLabelGroup.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace app\admin\model\shop;
+
+use app\common\Enum\StatusEnum;
+use think\Model;
+class GoodsLabelGroup extends Model
+{
+    protected $name = 'shop_goods_label_group';
+    protected $autoWriteTimestamp = 'int';
+    protected $createTime = 'createtime';
+    protected $updateTime = 'updatetime';
+    protected $deleteTime = 'deletetime';
+
+    protected $append = ['status_text'];
+
+    public function getStatusList()
+    {
+        return StatusEnum::getMap();
+    }
+
+    public function getStatusTextAttr($value, $data)
+    {
+        $value = $value ?: ($data['status'] ?? '');
+        $list = $this->getStatusList();
+        return $list[$value] ?? '';
+    }
+
+    public function labels()
+    {
+        return $this->hasMany('GoodsLabel', 'group_id', 'id');
+    }
+}

+ 60 - 0
application/admin/model/shop/Unit.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace app\admin\model\shop;
+
+use app\common\Enum\StatusEnum;
+use think\Model;
+use traits\model\SoftDelete;
+
+class Unit extends Model
+{
+
+    use SoftDelete;
+
+    
+
+    // 表名
+    protected $name = 'shop_goods_unit';
+    
+    // 自动写入时间戳字段
+    protected $autoWriteTimestamp = 'integer';
+
+    // 定义时间戳字段名
+    protected $createTime = 'createtime';
+    protected $updateTime = 'updatetime';
+    protected $deleteTime = 'deletetime';
+
+    // 追加属性
+    protected $append = [
+        'status_text'
+    ];
+    
+
+    protected static function init()
+    {
+        self::afterInsert(function ($row) {
+            if (!$row['weigh']) {
+                $pk = $row->getPk();
+                $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]);
+            }
+        });
+    }
+
+    
+    public function getStatusList()
+    {
+        return StatusEnum::getMap();
+    }
+
+
+    public function getStatusTextAttr($value, $data)
+    {
+        $value = $value ?: ($data['status'] ?? '');
+        $list = $this->getStatusList();
+        return $list[$value] ?? '';
+    }
+
+
+
+
+}

+ 50 - 0
application/admin/model/supplier/Category.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace app\admin\model\supplier;
+
+use think\Model;
+use traits\model\SoftDelete;
+
+class Category extends Model
+{
+
+    use SoftDelete;
+
+    
+
+    // 表名
+    protected $name = 'shop_goods_supplier_category';
+    
+    // 自动写入时间戳字段
+    protected $autoWriteTimestamp = 'integer';
+
+    // 定义时间戳字段名
+    protected $createTime = 'createtime';
+    protected $updateTime = 'updatetime';
+    protected $deleteTime = 'deletetime';
+
+    // 追加属性
+    protected $append = [
+
+    ];
+    
+
+    protected static function init()
+    {
+        self::afterInsert(function ($row) {
+            if (!$row['weigh']) {
+                $pk = $row->getPk();
+                $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]);
+            }
+        });
+    }
+
+    
+
+
+
+
+
+
+
+}

+ 55 - 0
application/admin/model/supplier/Index.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace app\admin\model\supplier;
+
+use think\Model;
+use traits\model\SoftDelete;
+
+class Index extends Model
+{
+
+    use SoftDelete;
+
+    
+
+    // 表名
+    protected $name = 'shop_goods_supplier';
+    
+    // 自动写入时间戳字段
+    protected $autoWriteTimestamp = 'integer';
+
+    // 定义时间戳字段名
+    protected $createTime = 'createtime';
+    protected $updateTime = 'updatetime';
+    protected $deleteTime = 'deletetime';
+
+    // 追加属性
+    protected $append = [
+
+    ];
+    
+
+    protected static function init()
+    {
+        self::afterInsert(function ($row) {
+            if (!$row['weigh']) {
+                $pk = $row->getPk();
+                $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]);
+            }
+        });
+    }
+
+    public function category()
+    {
+        return $this->belongsTo('app\admin\model\supplier\Category', 'supplier_category_id', 'id');
+    }
+
+    
+
+
+
+
+
+
+
+}

+ 27 - 0
application/admin/validate/shop/Unit.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace app\admin\validate\shop;
+
+use think\Validate;
+
+class Unit extends Validate
+{
+    /**
+     * 验证规则
+     */
+    protected $rule = [
+    ];
+    /**
+     * 提示消息
+     */
+    protected $message = [
+    ];
+    /**
+     * 验证场景
+     */
+    protected $scene = [
+        'add'  => [],
+        'edit' => [],
+    ];
+    
+}

+ 27 - 0
application/admin/validate/supplier/Category.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace app\admin\validate\supplier;
+
+use think\Validate;
+
+class Category extends Validate
+{
+    /**
+     * 验证规则
+     */
+    protected $rule = [
+    ];
+    /**
+     * 提示消息
+     */
+    protected $message = [
+    ];
+    /**
+     * 验证场景
+     */
+    protected $scene = [
+        'add'  => [],
+        'edit' => [],
+    ];
+    
+}

+ 27 - 0
application/admin/validate/supplier/Index.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace app\admin\validate\supplier;
+
+use think\Validate;
+
+class Index extends Validate
+{
+    /**
+     * 验证规则
+     */
+    protected $rule = [
+    ];
+    /**
+     * 提示消息
+     */
+    protected $message = [
+    ];
+    /**
+     * 验证场景
+     */
+    protected $scene = [
+        'add'  => [],
+        'edit' => [],
+    ];
+    
+}

+ 57 - 7
application/admin/view/shop/goods/add.html

@@ -312,6 +312,12 @@
                         </div>
                     </div>
                     <div class="form-group">
+                        <label class="control-label col-xs-12 col-sm-2">{:__('Label_ids')}:</label>
+                        <div class="col-xs-12 col-sm-8">
+                            <input id="c-label_ids" data-source="shop/goods_label/index" data-multiple="true" class="form-control selectpage" name="row[label_ids]" type="text">
+                        </div>
+                    </div>
+                    <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">{:__('Guarantee')}:</label>
                         <div class="col-xs-12 col-sm-8">
                             <input id="c-guarantee_ids" data-source="shop/guarantee/index" data-multiple="true" class="form-control selectpage" name="row[guarantee_ids]" type="text" value="">
@@ -320,13 +326,13 @@
                     <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">{:__('Unit')}:</label>
                         <div class="col-xs-12 col-sm-8">
-                            <input id="c-unit_id" data-source="shop/brand/index" class="form-control selectpage" name="row[unit_id]" type="text" value="">
+                            <input id="c-unit_id" data-source="shop/unit/index" class="form-control selectpage" name="row[unit_id]" type="text" value="">
                         </div>
                     </div>
                     <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">{:__('Supplier')}:</label>
                         <div class="col-xs-12 col-sm-8">
-                            <input id="c-supplier_id" data-source="shop/brand/index" class="form-control selectpage" name="row[supplier_id]" type="text" value="">
+                            <input id="c-supplier_id" data-rule="required" data-source="supplier/index" class="form-control selectpage" name="row[supplier_id]" type="text" value="">
                         </div>
                     </div>
                  
@@ -436,7 +442,7 @@
                                     <h3 class="panel-title">{:__('商品详情预览')}</h3>
                                 </div>
                                 <div class="panel-body">
-                                    <div id="content-preview" style="overflow-y: auto; border: 1px solid #e5e5e5; min-height: 450px; padding: 10px;"></div>
+                                    <div id="content-preview" style="overflow-y: auto; border: 1px solid #e5e5e5; height: 490px; padding: 10px;"></div>
                                 </div>
                             </div>
                         </div>
@@ -449,13 +455,34 @@
                     <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">{:__('Stock_show_type')}:</label>
                         <div class="col-xs-12 col-sm-8">
-
                             <div class="radio">
                             {foreach name="stockShowTypeList" item="vo"}
-                            <label for="row[stock_show_type]-{$key|htmlentities}"><input id="row[stock_show_type]-{$key|htmlentities}" data-rule="required" name="row[stock_show_type]" type="radio" value="{$key|htmlentities}" {in name="key" value="EXACT"}checked{/in} /> {$vo|htmlentities}</label>
+                            <label for="row[stock_show_type]-{$key|htmlentities}" style="margin-right: 15px;">
+                                <input id="row[stock_show_type]-{$key|htmlentities}" data-rule="required" name="row[stock_show_type]" type="radio" value="{$key|htmlentities}" {in name="key" value="EXACT"}checked{/in} /> {$vo|htmlentities}
+                                {if condition="$key == 'EXACT'"}
+                                <a href="javascript:;" class="btn btn-xs btn-info" data-toggle="popover" data-html="true" data-placement="bottom" 
+                                data-content="<div style='text-align:center; padding:8px; width:140px;'>
+                                    <div style='font-size:12px; margin-bottom:5px; color:#333; font-weight:bold;'>
+                                        精确库存显示效果
+                                    </div>
+                                    
+                                </div>">
+                                    <i class="fa fa-question-circle"></i>
+                                </a>
+                                {/if}
+                                {if condition="$key == 'SKETCHY'"}
+                                <a href="javascript:;" class="btn btn-xs btn-info" data-toggle="popover" data-html="true" data-placement="bottom" 
+                                data-content="<div style='text-align:center; padding:8px; width:140px;'>
+                                    <div style='font-size:12px; margin-bottom:5px; color:#333; font-weight:bold;'>
+                                        模糊库存显示效果
+                                    </div>                                    
+                                </div>">
+                                    <i class="fa fa-question-circle"></i>
+                                </a>
+                                {/if}
+                            </label>
                             {/foreach}
                             </div>
-
                         </div>
                     </div>
                     <div class="form-group">
@@ -463,7 +490,30 @@
                         <div class="col-xs-12 col-sm-8">
                             <div class="radio">
                             {foreach name="salesShowTypeList" item="vo"}
-                            <label for="row[sales_show_type]-{$key|htmlentities}"><input id="row[sales_show_type]-{$key|htmlentities}" data-rule="required" name="row[sales_show_type]" type="radio" value="{$key|htmlentities}" {in name="key" value="EXACT"}checked{/in} /> {$vo|htmlentities}</label>
+                            <label for="row[sales_show_type]-{$key|htmlentities}" style="margin-right: 15px;">
+                                <input id="row[sales_show_type]-{$key|htmlentities}" data-rule="required" name="row[sales_show_type]" type="radio" value="{$key|htmlentities}" {in name="key" value="EXACT"}checked{/in} /> {$vo|htmlentities}
+                                {if condition="$key == 'EXACT'"}
+                                <a href="javascript:;" class="btn btn-xs btn-info" data-toggle="popover" data-html="true" data-placement="bottom" 
+                                data-content="<div style='text-align:center; padding:8px; width:140px;'>
+                                    <div style='font-size:12px; margin-bottom:5px; color:#333; font-weight:bold;'>
+                                        精确销量显示效果
+                                    </div>
+                                </div>">
+                                    <i class="fa fa-question-circle"></i>
+                                </a>
+                                {/if}
+                                {if condition="$key == 'SKETCHY'"}
+                                <a href="javascript:;" class="btn btn-xs btn-info" data-toggle="popover" data-html="true" data-placement="bottom" 
+                                data-content="<div style='text-align:center; padding:8px; width:140px;'>
+                                    <div style='font-size:12px; margin-bottom:5px; color:#333; font-weight:bold;'>
+                                        模糊销量显示效果
+                                    </div>
+                                    
+                                </div>">
+                                    <i class="fa fa-question-circle"></i>
+                                </a>
+                                {/if}
+                            </label>
                             {/foreach}
                             </div>
                         </div>

+ 9 - 3
application/admin/view/shop/goods/edit.html

@@ -311,6 +311,12 @@
                         </div>
                     </div>
                     <div class="form-group">
+                        <label class="control-label col-xs-12 col-sm-2">{:__('Label_ids')}:</label>
+                        <div class="col-xs-12 col-sm-8">
+                            <input id="c-label_ids" data-source="shop/goods_label/index" data-multiple="true" class="form-control selectpage" name="row[label_ids]" type="text" value="{$row.label_ids}">
+                        </div>
+                    </div>
+                    <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">{:__('Guarantee')}:</label>
                         <div class="col-xs-12 col-sm-8">
                             <input id="c-guarantee_ids" data-source="shop/guarantee/index" data-multiple="true" class="form-control selectpage" name="row[guarantee_ids]" type="text" value="{$row.guarantee_ids}">
@@ -319,13 +325,13 @@
                     <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">{:__('Unit')}:</label>
                         <div class="col-xs-12 col-sm-8">
-                            <input id="c-unit_id" data-source="shop/brand/index" class="form-control selectpage" name="row[unit_id]" type="text" value="{$row.unit_id}">
+                            <input id="c-unit_id" data-source="shop/unit/index" class="form-control selectpage" name="row[unit_id]" type="text" value="{$row.unit_id}">
                         </div>
                     </div>
                     <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">{:__('Supplier')}:</label>
                         <div class="col-xs-12 col-sm-8">
-                            <input id="c-supplier_id" data-source="shop/brand/index" class="form-control selectpage" name="row[supplier_id]" type="text" value="{$row.supplier_id}">
+                            <input id="c-supplier_id" data-rule="required" data-source="supplier/index" class="form-control selectpage" name="row[supplier_id]" type="text" value="{$row.supplier_id}">
                         </div>
                     </div>
                  
@@ -435,7 +441,7 @@
                                     <h3 class="panel-title">{:__('商品详情预览')}</h3>
                                 </div>
                                 <div class="panel-body">
-                                    <div id="content-preview" style="overflow-y: auto; border: 1px solid #e5e5e5; min-height: 450px; padding: 10px;"></div>
+                                    <div id="content-preview" style="overflow-y: auto; border: 1px solid #e5e5e5; height: 490px; padding: 10px;"></div>
                                 </div>
                             </div>
                         </div>

+ 5 - 4
application/admin/view/shop/goods/index.html

@@ -18,7 +18,7 @@
                     <div id="toolbar" class="toolbar">
                         <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
                         <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('shop/goods/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
-                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('shop/goods/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
+                        <!-- <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('shop/goods/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a> -->
                         <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('shop/goods/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
 
                         <div class="dropdown btn-group {:$auth->check('shop/goods/multi')?'':'hide'}">
@@ -30,13 +30,14 @@
                         </div>
 
                         <a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('shop/goods/recyclebin')?'':'hide'}" href="shop/goods/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>
-                        <a href="javascript:;" class="btn btn-info btn-dialog {:$auth->check('shop/sku_template/index')?'':'hide'}" data-area='["80%","80%"]' data-url="{:url('shop.sku_template/index')}" title="{:__('规格模板')}" ><i class="fa fa-asterisk"></i> {:__('规格模板')}</a>
+                        <!-- <a href="javascript:;" class="btn btn-info btn-dialog {:$auth->check('shop/sku_template/index')?'':'hide'}" data-area='["80%","80%"]' data-url="{:url('shop.sku_template/index')}" title="{:__('规格模板')}" ><i class="fa fa-asterisk"></i> {:__('规格模板')}</a>
                         <a href="javascript:;" class="btn btn-primary btn-dialog {:$auth->check('shop/guarantee/index')?'':'hide'}" data-area='["80%","80%"]' data-url="{:url('shop.guarantee/index')}" title="{:__('服务保障')}" ><i class="fa fa-shield"></i> {:__('服务保障')}</a>
-                        <a href="javascript:;" class="btn btn-success btn-dialog {:$auth->check('shop/brand/index')?'':'hide'}" data-area='["80%","80%"]' data-url="{:url('shop.brand/index')}" title="{:__('品牌管理')}" ><i class="fa fa-shield"></i> {:__('品牌管理')}</a>
+                        <a href="javascript:;" class="btn btn-success btn-dialog {:$auth->check('shop/brand/index')?'':'hide'}" data-area='["80%","80%"]' data-url="{:url('shop.brand/index')}" title="{:__('品牌管理')}" ><i class="fa fa-shield"></i> {:__('品牌管理')}</a> -->
                     </div>
                     <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
                            data-operate-edit="{:$auth->check('shop/goods/edit')}" 
-                           data-operate-del="{:$auth->check('shop/goods/del')}" 
+                           data-operate-del="{:$auth->check('shop/goods/del')}"
+                           data-operate-copy="{:$auth->check('shop/goods/copy')}"
                            width="100%">
                     </table>
                 </div>

+ 114 - 0
application/admin/view/shop/goods_label/add.html

@@ -0,0 +1,114 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">标签名称:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" class="form-control" name="row[name]" type="text" placeholder="请输入标签名称" data-rule="required">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">标签分组:</label>
+        <div class="col-xs-12 col-sm-8">
+            <select id="c-group_id" class="form-control selectpicker" name="row[group_id]" data-rule="required">
+                <option value="">请选择标签分组</option>
+            </select>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">效果设置:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="styleTypeList" item="vo"}
+                <label for="row[style_type]-{$key|htmlentities}"><input id="row[style_type]-{$key|htmlentities}" data-rule="required" name="row[style_type]" type="radio" value="{$key|htmlentities}" {in name="key" value="diy"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+   
+        <div class="form-group" data-favisible="style_type='diy'">
+            <label class="control-label col-xs-12 col-sm-2">文字颜色:</label>
+            <div class="col-xs-12 col-sm-8">
+                <div class="input-group">
+                    <input id="c-text_color" class="form-control" name="row[color_json][text_color]" type="text" placeholder="若未设置颜色,则为默认色">
+                    <span class="input-group-btn">
+                        <button type="button" class="btn btn-default btn-color colorpicker" style="padding:0;margin-left:1px;">
+                            <img src="__CDN__/assets/addons/shop/img/colorful.png" height="29" alt="">
+                        </button>
+                        <span class="msg-box n-right" for="c-text_color"></span>
+                    </span>
+                </div>
+            </div>
+        </div>
+        <div class="form-group" data-favisible="style_type='diy'">
+            <label class="control-label col-xs-12 col-sm-2">背景颜色:</label>
+            <div class="col-xs-12 col-sm-8">
+                <div class="input-group">
+                    <input id="c-bg_color" class="form-control" name="row[color_json][bg_color]" type="text" placeholder="若未设置颜色,则为默认色">
+                    <span class="input-group-btn">
+                        <button type="button" class="btn btn-default btn-color colorpicker" style="padding:0;margin-left:1px;"><img src="__CDN__/assets/addons/shop/img/colorful.png" height="29" alt=""></button>
+                        <span class="msg-box n-right" for="c-bg_color"></span>
+                    </span>
+                </div>
+            </div>
+        </div>
+        <div class="form-group" data-favisible="style_type='diy'">
+            <label class="control-label col-xs-12 col-sm-2">边框颜色:</label>
+            <div class="col-xs-12 col-sm-8">
+                <div class="input-group">
+                    <input id="c-border_color" class="form-control" name="row[color_json][border_color]" type="text" placeholder="若未设置颜色,则为默认色">
+                    <span class="input-group-btn">
+                        <button type="button" class="btn btn-default btn-color colorpicker" style="padding:0;margin-left:1px;"><img src="__CDN__/assets/addons/shop/img/colorful.png" height="29" alt=""></button>
+                        <span class="msg-box n-right" for="c-border_color"></span>
+                    </span>
+                </div>
+            </div>
+        </div>
+    
+ 
+        <div class="form-group" data-favisible="style_type='icon'">
+            <label class="control-label col-xs-12 col-sm-2">上传图片:</label>
+            <div class="col-xs-12 col-sm-8">
+                <div class="input-group">
+                    <input id="c-icon" class="form-control" size="50" name="row[icon]" type="text">
+                    <div class="input-group-addon no-border no-padding">
+                        <span><button type="button" id="plupload-icon" class="btn btn-danger plupload" data-input-id="c-icon" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-icon"><i class="fa fa-upload"></i> 上传</button></span>
+                        <span><button type="button" id="fachoose-icon" class="btn btn-primary fachoose" data-input-id="c-icon" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> 选择</button></span>
+                    </div>
+                    <span class="msg-box n-right" for="c-icon"></span>
+                </div>
+                <ul class="row list-inline plupload-preview" id="p-icon"></ul>
+                <span class="help-block">建议尺寸:80px*30px,未上传将显示默认</span>
+            </div>
+        </div>
+    
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">状态:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="statusList" item="vo"}
+                <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="1"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">标签说明:</label>
+        <div class="col-xs-12 col-sm-8">
+            <textarea id="c-memo" class="form-control" name="row[memo]" cols="30" rows="3" placeholder="请输入标签说明"></textarea>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">排序:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh" class="form-control" name="row[weigh]" type="number" value="0" placeholder="请输入排序" data-rule="required">
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">确定</button>
+            <button type="reset" class="btn btn-default btn-embossed">重置</button>
+        </div>
+    </div>
+</form>

+ 112 - 0
application/admin/view/shop/goods_label/edit.html

@@ -0,0 +1,112 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">标签名称:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}" placeholder="请输入标签名称" data-rule="required">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">标签分组:</label>
+        <div class="col-xs-12 col-sm-8">
+            <select id="c-group_id" class="form-control selectpicker" name="row[group_id]" data-value="{$row.group_id}" data-rule="required">
+                <option value="">请选择标签分组</option>
+            </select>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">效果设置:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="styleTypeList" item="vo"}
+                <label for="row[style_type]-{$key|htmlentities}"><input id="row[style_type]-{$key|htmlentities}" data-rule="required" name="row[style_type]" type="radio" value="{$key|htmlentities}" {in name="key" value="$row.style_type"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+            </div>
+        </div>
+    </div>
+    <div class="form-group" data-favisible="style_type='diy'">
+        <label class="control-label col-xs-12 col-sm-2">文字颜色:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-text_color" class="form-control" name="row[color_json][text_color]" type="text" value="{$row.color_json.text_color|default=''}" placeholder="若未设置颜色,则为默认色">
+                <span class="input-group-btn">
+                    <button type="button" class="btn btn-default btn-color colorpicker" style="padding:0;margin-left:1px;">
+                        <img src="__CDN__/assets/addons/shop/img/colorful.png" height="29" alt="">
+                    </button>
+                    <span class="msg-box n-right" for="c-text_color"></span>
+                </span>
+            </div>
+        </div>
+    </div>
+    <div class="form-group" data-favisible="style_type='diy'">
+        <label class="control-label col-xs-12 col-sm-2">背景颜色:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-bg_color" class="form-control" name="row[color_json][bg_color]" type="text" value="{$row.color_json.bg_color|default=''}" placeholder="若未设置颜色,则为默认色">
+                <span class="input-group-btn">
+                    <button type="button" class="btn btn-default btn-color colorpicker" style="padding:0;margin-left:1px;"><img src="__CDN__/assets/addons/shop/img/colorful.png" height="29" alt=""></button>
+                    <span class="msg-box n-right" for="c-bg_color"></span>
+                </span>
+            </div>
+        </div>
+    </div>
+    <div class="form-group" data-favisible="style_type='diy'">
+        <label class="control-label col-xs-12 col-sm-2">边框颜色:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-border_color" class="form-control" name="row[color_json][border_color]" type="text" value="{$row.color_json.border_color|default=''}" placeholder="若未设置颜色,则为默认色">
+                <span class="input-group-btn">
+                    <button type="button" class="btn btn-default btn-color colorpicker" style="padding:0;margin-left:1px;"><img src="__CDN__/assets/addons/shop/img/colorful.png" height="29" alt=""></button>
+                    <span class="msg-box n-right" for="c-border_color"></span>
+                </span>
+            </div>
+        </div>
+    </div>
+
+
+    <div class="form-group" data-favisible="style_type='icon'">
+        <label class="control-label col-xs-12 col-sm-2">上传图片:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="input-group">
+                <input id="c-icon" class="form-control" size="50" name="row[icon]" type="text" value="{$row.icon|htmlentities}">
+                <div class="input-group-addon no-border no-padding">
+                    <span><button type="button" id="plupload-icon" class="btn btn-danger plupload" data-input-id="c-icon" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-icon"><i class="fa fa-upload"></i> 上传</button></span>
+                    <span><button type="button" id="fachoose-icon" class="btn btn-primary fachoose" data-input-id="c-icon" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> 选择</button></span>
+                </div>
+                <span class="msg-box n-right" for="c-icon"></span>
+            </div>
+            <ul class="row list-inline plupload-preview" id="p-icon"></ul>
+            <span class="help-block">建议尺寸:80px*30px,未上传将显示默认</span>
+        </div>
+    </div>
+
+<div class="form-group">
+    <label class="control-label col-xs-12 col-sm-2">状态:</label>
+    <div class="col-xs-12 col-sm-8">
+        <div class="radio">
+            {foreach name="statusList" item="vo"}
+            <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="$row.status"}checked{/in} /> {$vo|htmlentities}</label>
+            {/foreach}
+           
+        </div>
+    </div>
+</div>
+<div class="form-group">
+    <label class="control-label col-xs-12 col-sm-2">标签说明:</label>
+    <div class="col-xs-12 col-sm-8">
+        <textarea id="c-memo" class="form-control" name="row[memo]" cols="30" rows="3" placeholder="请输入标签说明">{$row.memo|htmlentities}</textarea>
+    </div>
+</div>
+<div class="form-group">
+    <label class="control-label col-xs-12 col-sm-2">排序:</label>
+    <div class="col-xs-12 col-sm-8">
+        <input id="c-weigh" class="form-control" name="row[weigh]" type="number" value="{$row.weigh|default='0'}" placeholder="请输入排序" data-rule="required">
+    </div>
+</div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">确定</button>
+            <button type="reset" class="btn btn-default btn-embossed">重置</button>
+        </div>
+    </div>
+</form>

+ 29 - 0
application/admin/view/shop/goods_label/index.html

@@ -0,0 +1,29 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}"><i class="fa fa-refresh"></i></a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('shop/goodslabel/add')?'':'hide'}" title="{:__('Add')}"><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('shop/goodslabel/edit')?'':'hide'}" title="{:__('Edit')}"><i class="fa fa-pencil"></i> {:__('Edit')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('shop/goodslabel/del')?'':'hide'}" title="{:__('Delete')}"><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        <div class="dropdown btn-group {:$auth->check('shop/goodslabel/multi')?'':'hide'}">
+                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
+                            <ul class="dropdown-menu text-left" role="menu">
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=1"><i class="fa fa-eye"></i> 启用</a></li>
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=0"><i class="fa fa-eye-slash"></i> 禁用</a></li>
+                            </ul>
+                        </div>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('shop/goodslabel/edit')}"
+                           data-operate-del="{:$auth->check('shop/goodslabel/del')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 32 - 0
application/admin/view/shop/goods_label_group/add.html

@@ -0,0 +1,32 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">分组名称:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" class="form-control" name="row[name]" type="text" placeholder="请输入分组名称" data-rule="required">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">排序:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-sort" class="form-control" name="row[sort]" type="number" value="0" placeholder="请输入排序" data-rule="required">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">状态:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="statusList" item="vo"}
+                <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="1"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">确定</button>
+            <button type="reset" class="btn btn-default btn-embossed">重置</button>
+        </div>
+    </div>
+</form>

+ 32 - 0
application/admin/view/shop/goods_label_group/edit.html

@@ -0,0 +1,32 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">分组名称:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}" placeholder="请输入分组名称" data-rule="required">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">排序:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-sort" class="form-control" name="row[sort]" type="number" value="{$row.sort}" placeholder="请输入排序" data-rule="required">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">状态:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="statusList" item="vo"}
+                <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="{$row.status}"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-success btn-embossed disabled">确定</button>
+            <button type="reset" class="btn btn-default btn-embossed">重置</button>
+        </div>
+    </div>
+</form>

+ 29 - 0
application/admin/view/shop/goods_label_group/index.html

@@ -0,0 +1,29 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}"><i class="fa fa-refresh"></i></a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('shop/goodslabelgroup/add')?'':'hide'}" title="{:__('Add')}"><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('shop/goodslabelgroup/edit')?'':'hide'}" title="{:__('Edit')}"><i class="fa fa-pencil"></i> {:__('Edit')}</a>
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('shop/goodslabelgroup/del')?'':'hide'}" title="{:__('Delete')}"><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        <div class="dropdown btn-group {:$auth->check('shop/goodslabelgroup/multi')?'':'hide'}">
+                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
+                            <ul class="dropdown-menu text-left" role="menu">
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=1"><i class="fa fa-eye"></i> 启用</a></li>
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=0"><i class="fa fa-eye-slash"></i> 禁用</a></li>
+                            </ul>
+                        </div>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('shop/goodslabelgroup/edit')}"
+                           data-operate-del="{:$auth->check('shop/goodslabelgroup/del')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 3 - 3
application/admin/view/shop/theme/index.html

@@ -69,9 +69,9 @@
             <div class="row">
                 <div class="col-xs-12 col-sm-12 tab-content">
                     <div style="padding:20px;position:relative;min-height:820px;display:flex;">
-                        <div class="mockup-bg">
+                        <!-- <div class="mockup-bg">
                             <iframe scrolling="auto" frameborder="0" src="{$Think.config.shop.mobileurl|default='__CDN__/assets/addons/shop/preview.html'}" class="iframe" id="previewiframe"></iframe>
-                        </div>
+                        </div> -->
 
                         <div class="" style="flex:1;">
                             <form id="config-form" action="" role="form" method="post">
@@ -268,7 +268,7 @@
                                 <div style="display: block;width:100%;border-top:1px solid #eee;padding-top:15px;">
                                     <div class="form-group">
                                         <div class="col-xs-12 col-sm-12">
-                                            <button type="button" class="btn btn-primary btn-preview" data-preview="1"><i class="fa fa-eye"></i> 预览</button>
+                                            <!-- <button type="button" class="btn btn-primary btn-preview" data-preview="1"><i class="fa fa-eye"></i> 预览</button> -->
                                             <button type="button" class="btn btn-success btn-save" data-preview="0"><i class="fa fa-check"></i> 保存</button>
                                         </div>
                                     </div>

+ 33 - 0
application/admin/view/shop/unit/add.html

@@ -0,0 +1,33 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh" class="form-control" name="row[weigh]"  type="number" value="0" >
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="statusList" item="vo"}
+            <label for="row[status]-{$key}"><input id="row[status]-{$key}" name="row[status]" type="radio" value="{$key}" {in name="key" value="1"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
+        </div>
+    </div>
+</form>

+ 33 - 0
application/admin/view/shop/unit/edit.html

@@ -0,0 +1,33 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh" class="form-control" name="row[weigh]" type="number" value="{$row.weigh|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            
+            <div class="radio">
+            {foreach name="statusList" item="vo"}
+            <label for="row[status]-{$key}"><input id="row[status]-{$key}" name="row[status]" type="radio" value="{$key}" {in name="key" value="$row.status"}checked{/in} /> {$vo}</label> 
+            {/foreach}
+            </div>
+
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
+        </div>
+    </div>
+</form>

+ 46 - 0
application/admin/view/shop/unit/index.html

@@ -0,0 +1,46 @@
+<div class="panel panel-default panel-intro">
+    
+    <div class="panel-heading">
+        {:build_heading(null,FALSE)}
+        <ul class="nav nav-tabs" data-field="status">
+            <li class="{:$Think.get.status === null ? 'active' : ''}"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
+            {foreach name="statusList" item="vo"}
+            <li class="{:$Think.get.status === (string)$key ? 'active' : ''}"><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
+            {/foreach}
+        </ul>
+    </div>
+
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('shop/unit/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <!-- <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('shop/unit/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a> -->
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('shop/unit/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        
+
+                        <div class="dropdown btn-group {:$auth->check('shop/unit/multi')?'':'hide'}">
+                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
+                            <ul class="dropdown-menu text-left" role="menu">
+                                {foreach name="statusList" item="vo"}
+                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:" data-params="status={$key}">{:__('Set status to ' . $key)}</a></li>
+                                {/foreach}
+                            </ul>
+                        </div>
+
+                        <a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('shop/unit/recyclebin')?'':'hide'}" href="shop/unit/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('shop/unit/edit')}"
+                           data-operate-del="{:$auth->check('shop/unit/del')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 25 - 0
application/admin/view/shop/unit/recyclebin.html

@@ -0,0 +1,25 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        {:build_toolbar('refresh')}
+                        <a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('shop/unit/restore')?'':'hide'}" href="javascript:;" data-url="shop/unit/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
+                        <a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('shop/unit/destroy')?'':'hide'}" href="javascript:;" data-url="shop/unit/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
+                        <a class="btn btn-success btn-restoreall {:$auth->check('shop/unit/restore')?'':'hide'}" href="javascript:;" data-url="shop/unit/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
+                        <a class="btn btn-danger btn-destroyall {:$auth->check('shop/unit/destroy')?'':'hide'}" href="javascript:;" data-url="shop/unit/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover"
+                           data-operate-restore="{:$auth->check('shop/unit/restore')}"
+                           data-operate-destroy="{:$auth->check('shop/unit/destroy')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 32 - 0
application/admin/view/supplier/category/add.html

@@ -0,0 +1,32 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh" data-rule="required" class="form-control" name="row[weigh]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">状态:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="statusList" item="vo"}
+                <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="1"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
+        </div>
+    </div>
+</form>

+ 32 - 0
application/admin/view/supplier/category/edit.html

@@ -0,0 +1,32 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh" data-rule="required" class="form-control" name="row[weigh]" type="number" value="{$row.weigh|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">状态:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="statusList" item="vo"}
+                <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="$row.status"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
+        </div>
+    </div>
+</form>

+ 29 - 0
application/admin/view/supplier/category/index.html

@@ -0,0 +1,29 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('supplier/category/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <!-- <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('supplier/category/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a> -->
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('supplier/category/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        
+
+                        
+
+                        <a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('supplier/category/recyclebin')?'':'hide'}" href="supplier/category/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('supplier/category/edit')}"
+                           data-operate-del="{:$auth->check('supplier/category/del')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 25 - 0
application/admin/view/supplier/category/recyclebin.html

@@ -0,0 +1,25 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        {:build_toolbar('refresh')}
+                        <a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('supplier/category/restore')?'':'hide'}" href="javascript:;" data-url="supplier/category/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
+                        <a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('supplier/category/destroy')?'':'hide'}" href="javascript:;" data-url="supplier/category/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
+                        <a class="btn btn-success btn-restoreall {:$auth->check('supplier/category/restore')?'':'hide'}" href="javascript:;" data-url="supplier/category/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
+                        <a class="btn btn-danger btn-destroyall {:$auth->check('supplier/category/destroy')?'':'hide'}" href="javascript:;" data-url="supplier/category/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover"
+                           data-operate-restore="{:$auth->check('supplier/category/restore')}"
+                           data-operate-destroy="{:$auth->check('supplier/category/destroy')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 117 - 0
application/admin/view/supplier/index/add.html

@@ -0,0 +1,117 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Code')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-code" data-rule="required" class="form-control" name="row[code]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Supplier_category_id')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-supplier_category_id" data-rule="required" data-source="supplier/category/index" class="form-control selectpage" name="row[supplier_category_id]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Contact')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-contact" data-rule="required" class="form-control" name="row[contact]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Mobile')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-mobile"  class="form-control" name="row[mobile]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Landline')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-landline"  class="form-control" name="row[landline]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Email')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-email" class="form-control" name="row[email]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">省市区:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="form-inline" data-toggle="cxselect" data-selects="province,city,area">
+                <select class="province form-control" name="province" data-url="ajax/area" >
+                    <option value="">请选择省份</option>
+                </select>
+                <select class="city form-control" name="city" data-url="ajax/area" >
+                    <option value="">请选择城市</option>
+                </select>
+                <select class="area form-control" name="area" data-url="ajax/area">
+                    <option value="">请选择区县</option>
+                </select>
+            </div>
+            <input type="hidden" name="row[province_id]" id="province_id_hidden">
+            <input type="hidden" name="row[city_id]" id="city_id_hidden">
+            <input type="hidden" name="row[district_id]" id="district_id_hidden">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Address')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-address"  class="form-control" name="row[address]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Bank_account')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-bank_account"  class="form-control" name="row[bank_account]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Bank')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-bank"  class="form-control" name="row[bank]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Cardholder_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-cardholder_name"  class="form-control" name="row[cardholder_name]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Tax_id')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-tax_id"  class="form-control" name="row[tax_id]" type="text" value="">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh"  class="form-control" name="row[weigh]" type="number" value="0">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">状态:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="statusList" item="vo"}
+                <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="1"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
+        </div>
+    </div>
+</form>

+ 119 - 0
application/admin/view/supplier/index/edit.html

@@ -0,0 +1,119 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Code')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-code" data-rule="required" class="form-control" name="row[code]" type="text" value="{$row.code|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Supplier_category_id')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-supplier_category_id" data-rule="required" data-source="supplier/category/index" class="form-control selectpage" name="row[supplier_category_id]" type="text" value="{$row.supplier_category_id|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Contact')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-contact" data-rule="required" class="form-control" name="row[contact]" type="text" value="{$row.contact|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Mobile')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-mobile"  class="form-control" name="row[mobile]" type="text" value="{$row.mobile|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Landline')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-landline"  class="form-control" name="row[landline]" type="text" value="{$row.landline|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Email')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-email"  class="form-control" name="row[email]" type="text" value="{$row.email|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">省市区:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="form-inline" data-toggle="cxselect" data-selects="province,city,area">
+                <select class="province form-control" name="province" data-url="ajax/area">
+                    <option value="{$row.province_id|htmlentities}">请选择省份</option>
+                </select>
+                <select class="city form-control" name="city" data-url="ajax/area">
+                    <option value="{$row.city_id|htmlentities}">请选择城市</option>
+                </select>
+                <select class="area form-control" name="area" data-url="ajax/area">
+                    <option value="{$row.district_id|htmlentities}">请选择区县</option>
+                </select>
+            </div>
+    
+
+            <input type="hidden" name="row[province_id]" id="province_id_hidden" value="{$row.province_id|htmlentities}">
+            <input type="hidden" name="row[city_id]" id="city_id_hidden" value="{$row.city_id|htmlentities}">
+            <input type="hidden" name="row[district_id]" id="district_id_hidden" value="{$row.district_id|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Address')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-address"  class="form-control" name="row[address]" type="text" value="{$row.address|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Bank_account')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-bank_account"  class="form-control" name="row[bank_account]" type="text" value="{$row.bank_account|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Bank')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-bank"  class="form-control" name="row[bank]" type="text" value="{$row.bank|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Cardholder_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-cardholder_name"  class="form-control" name="row[cardholder_name]" type="text" value="{$row.cardholder_name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Tax_id')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-tax_id"  data-source="tax/index" class="form-control selectpage" name="row[tax_id]" type="text" value="{$row.tax_id|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-weigh"  class="form-control" name="row[weigh]" type="number" value="{$row.weigh|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">状态:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                {foreach name="statusList" item="vo"}
+                <label for="row[status]-{$key|htmlentities}"><input id="row[status]-{$key|htmlentities}" data-rule="required" name="row[status]" type="radio" value="{$key|htmlentities}" {in name="key" value="$row.status"}checked{/in} /> {$vo|htmlentities}</label>
+                {/foreach}
+               
+            </div>
+        </div>
+    </div>
+    <div class="form-group layer-footer">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
+        </div>
+    </div>
+</form>

+ 29 - 0
application/admin/view/supplier/index/index.html

@@ -0,0 +1,29 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
+                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('supplier/index/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
+                        <!-- <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('supplier/index/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a> -->
+                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('supplier/index/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
+                        
+
+                        
+
+                        <a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('supplier/index/recyclebin')?'':'hide'}" href="supplier/index/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
+                           data-operate-edit="{:$auth->check('supplier/index/edit')}"
+                           data-operate-del="{:$auth->check('supplier/index/del')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 25 - 0
application/admin/view/supplier/index/recyclebin.html

@@ -0,0 +1,25 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        {:build_toolbar('refresh')}
+                        <a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('supplier/index/restore')?'':'hide'}" href="javascript:;" data-url="supplier/index/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
+                        <a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('supplier/index/destroy')?'':'hide'}" href="javascript:;" data-url="supplier/index/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
+                        <a class="btn btn-success btn-restoreall {:$auth->check('supplier/index/restore')?'':'hide'}" href="javascript:;" data-url="supplier/index/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
+                        <a class="btn btn-danger btn-destroyall {:$auth->check('supplier/index/destroy')?'':'hide'}" href="javascript:;" data-url="supplier/index/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover"
+                           data-operate-restore="{:$auth->check('supplier/index/restore')}"
+                           data-operate-destroy="{:$auth->check('supplier/index/destroy')}"
+                           width="100%">
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 5 - 0
application/api/controller/Goods.php

@@ -147,6 +147,11 @@ class Goods extends Base
     public function lists()
     {
         $param = $this->request->param();
+        // 增加验证器处理
+        $validate = new \app\api\validate\Goods();
+        if (!$validate->scene('lists')->check($param)) {
+            $this->error($validate->getError());
+        }
         $pageSize = (int)$this->request->param('pageSize', 10);
         $orderby = $this->request->param('orderby', 'weigh');
         $orderway = $this->request->param('orderway', 'desc');

+ 19 - 0
application/common/Enum/GoodsLabelEnum.php

@@ -0,0 +1,19 @@
+<?php
+namespace app\common\Enum;
+
+class GoodsLabelEnum
+{
+    // 样式类型
+    const STYLE_DIY  = 'diy';
+    const STYLE_ICON = 'icon';
+    public static function getMap()
+    {
+        return [
+            self::STYLE_DIY => '自定义',
+            self::STYLE_ICON => '图片',
+        ];
+    }
+
+
+    // 其它常量...
+}

+ 29 - 29
application/common/Service/lottery/LotteryChanceService.php

@@ -63,22 +63,22 @@ class LotteryChanceService
         $grantedChances = [];
         
         try {
-            // 获取所有正在进行的抽奖活动
+        // 获取所有正在进行的抽奖活动
             $activities = LotteryService::getRunningActivities();
-            
-            foreach ($activities as $activity) {
-                try {
-                    $chances = static::processActivityForOrder($activity, $orderInfo, $userId);
-                    if ($chances > 0) {
-                        $grantedChances[] = [
-                            'activity_id' => $activity->id,
-                            'activity_name' => $activity->name,
+        
+        foreach ($activities as $activity) {
+            try {
+                $chances = static::processActivityForOrder($activity, $orderInfo, $userId);
+                if ($chances > 0) {
+                    $grantedChances[] = [
+                        'activity_id' => $activity->id,
+                        'activity_name' => $activity->name,
                             'chances' => $chances,
                             'granted_time' => time()
-                        ];
-                    }
-                } catch (Exception $e) {
-                    // 记录错误但不影响其他活动的处理
+                    ];
+                }
+            } catch (Exception $e) {
+                // 记录错误但不影响其他活动的处理
                     trace("抽奖机会分发失败 - 活动ID: {$activity->id}, 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error');
                 }
             }
@@ -107,22 +107,22 @@ class LotteryChanceService
         $grantedChances = [];
         
         try {
-            // 获取所有正在进行的抽奖活动
+        // 获取所有正在进行的抽奖活动
             $activities = LotteryService::getRunningActivities();
-            
-            foreach ($activities as $activity) {
-                try {
-                    $chances = static::processActivityForRecharge($activity, $rechargeInfo, $userId);
-                    if ($chances > 0) {
-                        $grantedChances[] = [
-                            'activity_id' => $activity->id,
-                            'activity_name' => $activity->name,
+        
+        foreach ($activities as $activity) {
+            try {
+                $chances = static::processActivityForRecharge($activity, $rechargeInfo, $userId);
+                if ($chances > 0) {
+                    $grantedChances[] = [
+                        'activity_id' => $activity->id,
+                        'activity_name' => $activity->name,
                             'chances' => $chances,
                             'granted_time' => time()
-                        ];
-                    }
-                } catch (Exception $e) {
-                    // 记录错误但不影响其他活动的处理
+                    ];
+                }
+            } catch (Exception $e) {
+                // 记录错误但不影响其他活动的处理
                     trace("充值抽奖机会分发失败 - 活动ID: {$activity->id}, 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error');
                 }
             }
@@ -585,7 +585,7 @@ class LotteryChanceService
                               ->where('status', 1)
                               ->order('id', 'asc')
                               ->select();
-    }
+        }
 
     /**
      * 检查条件是否可重复获得奖励
@@ -631,7 +631,7 @@ class LotteryChanceService
                     'condition_type_text' => $condition->type_text,
                     'reward_times' => static::getRewardTimes($condition),
                     'can_repeat' => static::canRepeat($condition)
-                ];
+        ];
             }
         }
 
@@ -1013,7 +1013,7 @@ class LotteryChanceService
                 $qualifiedUsers[] = $userId;
             }
         }
-        
+
         return $qualifiedUsers;
     }
 

BIN
public/assets/img/goods/sales-exact.png


BIN
public/assets/img/goods/sales-sketchy.png


BIN
public/assets/img/goods/stock-exact.png


BIN
public/assets/img/goods/stock-sketchy.png


+ 57 - 39
public/assets/js/backend/shop/goods.js

@@ -6,30 +6,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
     var vm, si;
 
     var Controller = {
-        index: function () {
-            // 解析JSON格式的Config数据
-            var statusList = {};
-            var specTypeList = {};
-            var goodsTypeList = {};
-            
-            try {
-                statusList = Config.statusList ? JSON.parse(Config.statusList) : {};
-                specTypeList = Config.specTypeList ? JSON.parse(Config.specTypeList) : {};
-                goodsTypeList = Config.goodsTypeList ? JSON.parse(Config.goodsTypeList) : {};
-            } catch (e) {
-                console.error('解析Config数据失败:', e);
-                statusList = {"normal": "正常", "hidden": "隐藏", "soldout": "售罄"};
-                specTypeList = {0: "单规格", 1: "多规格"};
-                goodsTypeList = {1: "普通商品"};
-            }
-            
-            // 调试输出Config数据
-            console.log('解析后的Config数据:', {
-                statusList: statusList,
-                specTypeList: specTypeList,
-                goodsTypeList: goodsTypeList
-            });
-            
+        index: function () {         
             // 添加列表样式优化
             $('<style>')
                 .prop('type', 'text/css')
@@ -137,7 +114,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
             });
             Table.config.dragsortfield = '';
             var table = $("#table");
-
+            var goodsTypeList = Controller.api.parseConfigJson('goodsTypeList', {});
+            var specTypeList = Controller.api.parseConfigJson('specTypeList', {});
+            var statusList = Controller.api.parseConfigJson('statusList', {});
             // 初始化表格
             table.bootstrapTable({
                 url: $.fn.bootstrapTable.defaults.extend.index_url,
@@ -2230,6 +2209,21 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
 
 
         api: {
+             // 解析Config中的JSON字符串的辅助函数
+             parseConfigJson: function(configKey, defaultValue) {
+                var configValue = Config[configKey] || defaultValue || {};
+                
+                // 如果是字符串,尝试解析JSON
+                if (typeof configValue === 'string') {
+                    try {
+                        return JSON.parse(configValue);
+                    } catch (e) {
+                        return defaultValue || {};
+                    }
+                }
+                
+                return configValue;
+            },
             // 初始化规格类型控制
             initSpecTypeControl: function() {
                 // 监听规格类型切换
@@ -2264,6 +2258,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
             },
             
             bindevent: function () {
+                // 初始化 popover 组件,用于显示库存和销量显示类型的tip图片
+                $('[data-toggle="popover"]').popover({
+                    trigger: 'hover',
+                    html: true,
+                    container: 'body'
+                });
 
                 $(document).on("click", ".btn-legal", function (a) {
                     Fast.api.ajax({
@@ -2612,24 +2612,42 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                                         for (let item of Config.goods_skus) {
                                             console.log('处理SKU项:', item);
                                             if (item.sku_attr) {
-                                                console.log('SKU属性字符串:', item.sku_attr);
-                                                let sku_attr = item.sku_attr.split(',');
-                                                console.log('分割后的属性数组:', sku_attr);
+                                                console.log('SKU属性数据:', item.sku_attr);
                                                 let attr = [];
-                                                for (let res of sku_attr) {
-                                                    let sku = res.split(':');
-                                                    console.log('属性键值对:', sku);
-                                                    if (sku.length >= 2) {
-                                                        attr.push(sku[1]);
+                                                let skuAttrs = [];
+                                                
+                                                // 尝试解析JSON格式
+                                                try {
+                                                    skuAttrs = JSON.parse(item.sku_attr);
+                                                    console.log('解析JSON格式成功:', skuAttrs);
+                                                } catch (e) {
+                                                    // 如果JSON解析失败,尝试旧的字符串格式
+                                                    console.log('JSON解析失败,尝试字符串格式:', e);
+                                                    let sku_attr = item.sku_attr.split(',');
+                                                    skuAttrs = [];
+                                                    for (let res of sku_attr) {
+                                                        let sku = res.split(':');
+                                                        if (sku.length >= 2) {
+                                                            skuAttrs.push({
+                                                                name: sku[0],
+                                                                value: sku[1]
+                                                            });
+                                                        }
+                                                    }
+                                                    console.log('转换字符串格式为对象:', skuAttrs);
+                                                }
+                                                
+                                                // 处理规格属性数组
+                                                for (let attrObj of skuAttrs) {
+                                                    if (attrObj.name && attrObj.value) {
+                                                        attr.push(attrObj.value);
                                                         //属性名
-                                                        if (!specName[sku[0]]) {
-                                                            specName[sku[0]] = [];
+                                                        if (!specName[attrObj.name]) {
+                                                            specName[attrObj.name] = [];
                                                         }
-                                                        if (!specName[sku[0]].includes(sku[1])) {
-                                                            specName[sku[0]].push(sku[1]);
+                                                        if (!specName[attrObj.name].includes(attrObj.value)) {
+                                                            specName[attrObj.name].push(attrObj.value);
                                                         }
-                                                    } else {
-                                                        console.warn('无效的属性格式:', res);
                                                     }
                                                 }
                                                 // 使用字符串作为键,与renderTableData中的查找方式一致

+ 118 - 0
public/assets/js/backend/shop/goods_label.js

@@ -0,0 +1,118 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+    var Controller = {
+        index: function () {
+            Table.api.init({
+                extend: {
+                    index_url: 'shop/goods_label/index' + location.search,
+                    add_url: 'shop/goods_label/add',
+                    edit_url: 'shop/goods_label/edit',
+                    del_url: 'shop/goods_label/del',
+                    multi_url: 'shop/goods_label/multi',
+                    import_url: 'shop/goods_label/import',
+                    table: 'shop_goods_label',
+                }
+            });
+            var table = $("#table");
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'weigh',
+                sortOrder: 'desc',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id'), operate: false},
+                        {field: 'name', title: __('Name'), operate: 'LIKE', formatter: function(value, row, index) {
+                            if (row.style_type === 'diy') {
+                                // 自定义样式,显示带颜色的标签
+                                var colorJson = {};
+                                try {
+                                    colorJson = typeof row.color_json === 'string' ? JSON.parse(row.color_json) : (row.color_json || {});
+                                } catch (e) {
+                                    colorJson = {};
+                                }
+                                var style = '';
+                                if (colorJson.text_color) style += 'color: ' + colorJson.text_color + ';';
+                                if (colorJson.bg_color) style += 'background-color: ' + colorJson.bg_color + ';';
+                                if (colorJson.border_color) style += 'border: 1px solid ' + colorJson.border_color + ';';
+                                style += 'padding: 2px 8px; border-radius: 3px; display: inline-block;';
+                                return '<span style="' + style + '">' + value + '</span>';
+                            } else if (row.style_type === 'icon') {
+                                // 图片样式,有图片显示图片,没有显示名称
+                                if (row.icon && row.icon.trim() !== '') {
+                                    return '<img src="' + Fast.api.cdnurl(row.icon) + '" style="max-height: 30px; max-width: 80px;" alt="' + value + '">';
+                                } else {
+                                    return value;
+                                }
+                            }
+                            return value;
+                        }},
+                        {field: 'group.name', title: __('Label group'), searchList:  $.getJSON("shop/goodslabelgroup/getList")},
+                        {field: 'memo', title: __('Label memo'), operate: 'LIKE'},
+                        {field: 'weigh', title: __('Weigh'), operate: false},
+                        {field: 'status', title: __('Status'), searchList: Controller.api.parseConfigJson('statusList'), formatter: Table.api.formatter.status},
+                        {field: 'createtime', title: __('Createtime'), operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime},
+                        {field: 'updatetime', title: __('Updatetime'), operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime, visible: false},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+            Table.api.bindevent(table);
+        },
+        add: function () { Controller.api.bindevent(); },
+        edit: function () { Controller.api.bindevent(); },
+        api: {
+            // 解析Config中的JSON字符串的辅助函数
+            parseConfigJson: function(configKey, defaultValue) {
+                var configValue = Config[configKey] || defaultValue || {};
+                
+                // 如果是字符串,尝试解析JSON
+                if (typeof configValue === 'string') {
+                    try {
+                        return JSON.parse(configValue);
+                    } catch (e) {
+                        return defaultValue || {};
+                    }
+                }
+                
+                return configValue;
+            },
+            bindevent: function () {
+                // 颜色选择器初始化
+                require(['jquery-colorpicker'], function () {
+                    $('.colorpicker').colorpicker({
+                        color: function (btn) {
+                            var color = "#000000";
+                            var rgb = $(this.eve).val().match(/^rgb\(((\d+),\s*(\d+),\s*(\d+))\)$/);
+                            if (rgb) {
+                                color = rgb[1];
+                            }
+                            return color;
+                        }
+                    }, function (btn, obj) {
+                        $(btn).closest(".input-group").find("input").val('#' + obj.hex).css('backgroundColor', '#' + obj.hex);
+                    }, function (btn, obj) {
+                        $(btn).closest(".input-group").find("input").val('').css('backgroundColor', '#' + obj.hex);
+                    });
+                });
+                // 动态加载标签分组下拉
+                var groupSelect = $('#c-group_id');
+                if (groupSelect.length) {
+                    $.get('shop/goodslabelgroup/index', function (res) {
+                        if (res && res.rows) {
+                            $.each(res.rows, function (i, item) {
+                                groupSelect.append('<option value=\"' + item.id + '\">' + item.name + '</option>');
+                            });
+                            if (typeof groupSelect.data('value') !== 'undefined') {
+                                groupSelect.val(groupSelect.data('value'));
+                            }
+                            groupSelect.selectpicker('refresh');
+                        }
+                    });
+                }
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 58 - 0
public/assets/js/backend/shop/goods_label_group.js

@@ -0,0 +1,58 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+    var Controller = {
+        index: function () {
+            Table.api.init({
+                extend: {
+                    index_url: 'shop/goods_label_group/index' + location.search,
+                    add_url: 'shop/goods_label_group/add',
+                    edit_url: 'shop/goods_label_group/edit',
+                    del_url: 'shop/goods_label_group/del',
+                    multi_url: 'shop/goods_label_group/multi',
+                    import_url: 'shop/goods_label_group/import',
+                    table: 'shop_goods_label_group',
+                }
+            });
+            var table = $("#table");
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'sort',
+                sortOrder: 'desc',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id'), operate: false},
+                        {field: 'name', title: __('Name'), operate: 'LIKE'},
+                        {field: 'sort', title: __('Sort'), operate: false},
+                        {field: 'status', title: __('Status'), searchList: Controller.api.parseConfigJson('statusList'), formatter: Table.api.formatter.status},
+                        {field: 'createtime', title: __('Createtime'), operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime},
+                        {field: 'updatetime', title: __('Updatetime'), operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime, visible: false},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+            Table.api.bindevent(table);
+        },
+        add: function () { Controller.api.bindevent(); },
+        edit: function () { Controller.api.bindevent(); },
+        api: {
+             // 解析Config中的JSON字符串的辅助函数
+             parseConfigJson: function(configKey, defaultValue) {
+                var configValue = Config[configKey] || defaultValue || {};
+                
+                // 如果是字符串,尝试解析JSON
+                if (typeof configValue === 'string') {
+                    try {
+                        return JSON.parse(configValue);
+                    } catch (e) {
+                        return defaultValue || {};
+                    }
+                }
+                
+                return configValue;
+            },
+             bindevent: function () { Form.api.bindevent($("form[role=form]")); } 
+            }
+    };
+    return Controller;
+});

+ 131 - 0
public/assets/js/backend/shop/unit.js

@@ -0,0 +1,131 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'shop/unit/index' + location.search,
+                    add_url: 'shop/unit/add',
+                    edit_url: 'shop/unit/edit',
+                    del_url: 'shop/unit/del',
+                    multi_url: 'shop/unit/multi',
+                    import_url: 'shop/unit/import',
+                    table: 'shop_goods_unit',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'weigh',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'name', title: __('Name'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        {field: 'weigh', title: __('Weigh'), operate: false},
+                        {field: 'status', title: __('Status'), searchList: Controller.api.parseConfigJson('statusList'), formatter: Table.api.formatter.status},
+                        {field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
+                        {field: 'updatetime', title: __('Updatetime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        recyclebin: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    'dragsort_url': ''
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: 'shop/unit/recyclebin' + location.search,
+                pk: 'id',
+                sortName: 'id',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'name', title: __('Name'), align: 'left'},
+                        {
+                            field: 'deletetime',
+                            title: __('Deletetime'),
+                            operate: 'RANGE',
+                            addclass: 'datetimerange',
+                            formatter: Table.api.formatter.datetime
+                        },
+                        {
+                            field: 'operate',
+                            width: '140px',
+                            title: __('Operate'),
+                            table: table,
+                            events: Table.api.events.operate,
+                            buttons: [
+                                {
+                                    name: 'Restore',
+                                    text: __('Restore'),
+                                    classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
+                                    icon: 'fa fa-rotate-left',
+                                    url: 'shop/unit/restore',
+                                    refresh: true
+                                },
+                                {
+                                    name: 'Destroy',
+                                    text: __('Destroy'),
+                                    classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
+                                    icon: 'fa fa-times',
+                                    url: 'shop/unit/destroy',
+                                    refresh: true
+                                }
+                            ],
+                            formatter: Table.api.formatter.operate
+                        }
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+               // 解析Config中的JSON字符串的辅助函数
+            parseConfigJson: function(configKey, defaultValue) {
+            var configValue = Config[configKey] || defaultValue || {};
+            
+            // 如果是字符串,尝试解析JSON
+            if (typeof configValue === 'string') {
+                try {
+                    return JSON.parse(configValue);
+                } catch (e) {
+                    return defaultValue || {};
+                }
+            }
+            
+            return configValue;
+            },
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 131 - 0
public/assets/js/backend/supplier/category.js

@@ -0,0 +1,131 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'supplier/category/index' + location.search,
+                    add_url: 'supplier/category/add',
+                    edit_url: 'supplier/category/edit',
+                    del_url: 'supplier/category/del',
+                    multi_url: 'supplier/category/multi',
+                    import_url: 'supplier/category/import',
+                    table: 'shop_goods_supplier_category',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'weigh',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'name', title: __('Name'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        {field: 'weigh', title: __('Weigh'), operate: false},
+                        {field: 'status', title: __('Status'), searchList: Controller.api.parseConfigJson('statusList'), formatter: Table.api.formatter.status},
+                        {field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
+                        {field: 'updatetime', title: __('Updatetime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        recyclebin: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    'dragsort_url': ''
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: 'supplier/category/recyclebin' + location.search,
+                pk: 'id',
+                sortName: 'id',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'name', title: __('Name'), align: 'left'},
+                        {
+                            field: 'deletetime',
+                            title: __('Deletetime'),
+                            operate: 'RANGE',
+                            addclass: 'datetimerange',
+                            formatter: Table.api.formatter.datetime
+                        },
+                        {
+                            field: 'operate',
+                            width: '140px',
+                            title: __('Operate'),
+                            table: table,
+                            events: Table.api.events.operate,
+                            buttons: [
+                                {
+                                    name: 'Restore',
+                                    text: __('Restore'),
+                                    classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
+                                    icon: 'fa fa-rotate-left',
+                                    url: 'supplier/category/restore',
+                                    refresh: true
+                                },
+                                {
+                                    name: 'Destroy',
+                                    text: __('Destroy'),
+                                    classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
+                                    icon: 'fa fa-times',
+                                    url: 'supplier/category/destroy',
+                                    refresh: true
+                                }
+                            ],
+                            formatter: Table.api.formatter.operate
+                        }
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+                // 解析Config中的JSON字符串的辅助函数
+            parseConfigJson: function(configKey, defaultValue) {
+            var configValue = Config[configKey] || defaultValue || {};
+            
+            // 如果是字符串,尝试解析JSON
+            if (typeof configValue === 'string') {
+                try {
+                    return JSON.parse(configValue);
+                } catch (e) {
+                    return defaultValue || {};
+                }
+            }
+            
+            return configValue;
+            },
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 186 - 0
public/assets/js/backend/supplier/index.js

@@ -0,0 +1,186 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'supplier/index/index' + location.search,
+                    add_url: 'supplier/index/add',
+                    edit_url: 'supplier/index/edit',
+                    del_url: 'supplier/index/del',
+                    multi_url: 'supplier/index/multi',
+                    import_url: 'supplier/index/import',
+                    table: 'shop_goods_supplier',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'weigh',
+                fixedColumns: true,
+                fixedRightNumber: 1,
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'name', title: __('Name'), operate: 'LIKE'},
+                        // {field: 'code', title: __('Code'), operate: 'LIKE'},
+                        {field: 'category.name', title: __('Supplier_category_id')},
+                        {field: 'contact', title: __('Contact'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        {field: 'mobile', title: __('Mobile'), operate: 'LIKE'},
+                        {field: 'landline', title: __('Landline'), operate: 'LIKE'},
+                        // {field: 'email', title: __('Email'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        // {field: 'province_id', title: __('Province_id')},
+                        // {field: 'city_id', title: __('City_id')},
+                        // {field: 'district_id', title: __('District_id')},
+                        // {field: 'address', title: __('Address'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        // {field: 'bank_account', title: __('Bank_account'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        // {field: 'bank', title: __('Bank'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        // {field: 'cardholder_name', title: __('Cardholder_name'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
+                        // {field: 'tax_id', title: __('Tax_id'), operate: 'LIKE'},
+                        {field: 'weigh', title: __('Weigh'), operate: false},
+                        {field: 'status', title: __('Status'), searchList: Controller.api.parseConfigJson('statusList'), formatter: Table.api.formatter.status},
+                        {field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
+                        {field: 'updatetime', title: __('Updatetime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        recyclebin: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    'dragsort_url': ''
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: 'supplier/index/recyclebin' + location.search,
+                pk: 'id',
+                sortName: 'id',
+                columns: [
+                    [
+                        {checkbox: true},
+                        {field: 'id', title: __('Id')},
+                        {field: 'name', title: __('Name'), align: 'left'},
+                        {
+                            field: 'deletetime',
+                            title: __('Deletetime'),
+                            operate: 'RANGE',
+                            addclass: 'datetimerange',
+                            formatter: Table.api.formatter.datetime
+                        },
+                        {
+                            field: 'operate',
+                            width: '140px',
+                            title: __('Operate'),
+                            table: table,
+                            events: Table.api.events.operate,
+                            buttons: [
+                                {
+                                    name: 'Restore',
+                                    text: __('Restore'),
+                                    classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
+                                    icon: 'fa fa-rotate-left',
+                                    url: 'supplier/index/restore',
+                                    refresh: true
+                                },
+                                {
+                                    name: 'Destroy',
+                                    text: __('Destroy'),
+                                    classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
+                                    icon: 'fa fa-times',
+                                    url: 'supplier/index/destroy',
+                                    refresh: true
+                                }
+                            ],
+                            formatter: Table.api.formatter.operate
+                        }
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            // 解析Config中的JSON字符串的辅助函数
+            parseConfigJson: function(configKey, defaultValue) {
+            var configValue = Config[configKey] || defaultValue || {};
+            
+            // 如果是字符串,尝试解析JSON
+            if (typeof configValue === 'string') {
+                try {
+                    return JSON.parse(configValue);
+                } catch (e) {
+                    return defaultValue || {};
+                }
+            }
+            
+            return configValue;
+            },
+            bindevent: function () {
+                // 省市区联动同步
+                $(document).on('change', 'select[name="province"]', function() {
+                    $('#province_id_hidden').val($(this).val());
+                });
+                $(document).on('change', 'select[name="city"]', function() {
+                    $('#city_id_hidden').val($(this).val());
+                });
+                $(document).on('change', 'select[name="area"]', function() {
+                    $('#district_id_hidden').val($(this).val());
+                });
+                
+                // 编辑页面初始化省市区选中状态
+                // if ($('#province_id_hidden').length > 0) {
+                //     var provinceId = $('#province_id_hidden').val();
+                //     var cityId = $('#city_id_hidden').val();
+                //     var districtId = $('#district_id_hidden').val();
+                    
+                //     if (provinceId) {
+                //         // 设置省份选中状态
+                //         setTimeout(function() {
+                //             $('select[name="province"]').val(provinceId).trigger('change');
+                            
+                //             // 如果有城市ID,等待省份联动完成后设置城市
+                //             if (cityId) {
+                //                 setTimeout(function() {
+                //                     $('select[name="city"]').val(cityId).trigger('change');
+                                    
+                //                     // 如果有区县ID,等待城市联动完成后设置区县
+                //                     if (districtId) {
+                //                         setTimeout(function() {
+                //                             $('select[name="area"]').val(districtId);
+                //                         }, 500);
+                //                     }
+                //                 }, 500);
+                //             }
+                //         }, 500);
+                //     }
+                // }
+                
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});